xref: /openbmc/qemu/qapi/string-output-visitor.c (revision 31e40415)
1a020f980SPaolo Bonzini /*
2a020f980SPaolo Bonzini  * String printing Visitor
3a020f980SPaolo Bonzini  *
408f9541dSEric Blake  * Copyright Red Hat, Inc. 2012-2016
5a020f980SPaolo Bonzini  *
6a020f980SPaolo Bonzini  * Author: Paolo Bonzini <pbonzini@redhat.com>
7a020f980SPaolo Bonzini  *
8a020f980SPaolo Bonzini  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
9a020f980SPaolo Bonzini  * See the COPYING.LIB file in the top-level directory.
10a020f980SPaolo Bonzini  *
11a020f980SPaolo Bonzini  */
12a020f980SPaolo Bonzini 
13cbf21151SPeter Maydell #include "qemu/osdep.h"
14*31e40415SPhilippe Mathieu-Daudé #include "qemu/cutils.h"
157b1b5d19SPaolo Bonzini #include "qapi/string-output-visitor.h"
167b1b5d19SPaolo Bonzini #include "qapi/visitor-impl.h"
170b7593e0SPaolo Bonzini #include "qemu/host-utils.h"
18e41b509dSPaolo Bonzini #include <math.h>
1969e25563SHu Tao #include "qemu/range.h"
2069e25563SHu Tao 
2169e25563SHu Tao enum ListMode {
2269e25563SHu Tao     LM_NONE,             /* not traversing a list of repeated options */
23d9f62ddeSEric Blake     LM_STARTED,          /* next_list() ready to be called */
2469e25563SHu Tao 
2569e25563SHu Tao     LM_IN_PROGRESS,      /* next_list() has been called.
2669e25563SHu Tao                           *
2769e25563SHu Tao                           * Generating the next list link will consume the most
2869e25563SHu Tao                           * recently parsed QemuOpt instance of the repeated
2969e25563SHu Tao                           * option.
3069e25563SHu Tao                           *
3169e25563SHu Tao                           * Parsing a value into the list link will examine the
3269e25563SHu Tao                           * next QemuOpt instance of the repeated option, and
3369e25563SHu Tao                           * possibly enter LM_SIGNED_INTERVAL or
3469e25563SHu Tao                           * LM_UNSIGNED_INTERVAL.
3569e25563SHu Tao                           */
3669e25563SHu Tao 
3769e25563SHu Tao     LM_SIGNED_INTERVAL,  /* next_list() has been called.
3869e25563SHu Tao                           *
3969e25563SHu Tao                           * Generating the next list link will consume the most
4069e25563SHu Tao                           * recently stored element from the signed interval,
4169e25563SHu Tao                           * parsed from the most recent QemuOpt instance of the
4269e25563SHu Tao                           * repeated option. This may consume QemuOpt itself
4369e25563SHu Tao                           * and return to LM_IN_PROGRESS.
4469e25563SHu Tao                           *
4569e25563SHu Tao                           * Parsing a value into the list link will store the
4669e25563SHu Tao                           * next element of the signed interval.
4769e25563SHu Tao                           */
4869e25563SHu Tao 
4969e25563SHu Tao     LM_UNSIGNED_INTERVAL,/* Same as above, only for an unsigned interval. */
5069e25563SHu Tao 
51d9f62ddeSEric Blake     LM_END,              /* next_list() called, about to see last element. */
5269e25563SHu Tao };
5369e25563SHu Tao 
5469e25563SHu Tao typedef enum ListMode ListMode;
55a020f980SPaolo Bonzini 
56a020f980SPaolo Bonzini struct StringOutputVisitor
57a020f980SPaolo Bonzini {
58a020f980SPaolo Bonzini     Visitor visitor;
590b7593e0SPaolo Bonzini     bool human;
6069e25563SHu Tao     GString *string;
613b098d56SEric Blake     char **result;
6269e25563SHu Tao     ListMode list_mode;
6369e25563SHu Tao     union {
6469e25563SHu Tao         int64_t s;
6569e25563SHu Tao         uint64_t u;
6669e25563SHu Tao     } range_start, range_end;
6769e25563SHu Tao     GList *ranges;
681158bb2aSEric Blake     void *list; /* Only needed for sanity checking the caller */
69a020f980SPaolo Bonzini };
70a020f980SPaolo Bonzini 
71d7bea75dSEric Blake static StringOutputVisitor *to_sov(Visitor *v)
72d7bea75dSEric Blake {
73d7bea75dSEric Blake     return container_of(v, StringOutputVisitor, visitor);
74d7bea75dSEric Blake }
75d7bea75dSEric Blake 
76a020f980SPaolo Bonzini static void string_output_set(StringOutputVisitor *sov, char *string)
77a020f980SPaolo Bonzini {
7869e25563SHu Tao     if (sov->string) {
7969e25563SHu Tao         g_string_free(sov->string, true);
8069e25563SHu Tao     }
8169e25563SHu Tao     sov->string = g_string_new(string);
8269e25563SHu Tao     g_free(string);
8369e25563SHu Tao }
8469e25563SHu Tao 
8569e25563SHu Tao static void string_output_append(StringOutputVisitor *sov, int64_t a)
8669e25563SHu Tao {
8769e25563SHu Tao     Range *r = g_malloc0(sizeof(*r));
88a0efbf16SMarkus Armbruster 
89a0efbf16SMarkus Armbruster     range_set_bounds(r, a, a);
907c47959dSEric Blake     sov->ranges = range_list_insert(sov->ranges, r);
9169e25563SHu Tao }
9269e25563SHu Tao 
9369e25563SHu Tao static void string_output_append_range(StringOutputVisitor *sov,
9469e25563SHu Tao                                        int64_t s, int64_t e)
9569e25563SHu Tao {
9669e25563SHu Tao     Range *r = g_malloc0(sizeof(*r));
97a0efbf16SMarkus Armbruster 
98a0efbf16SMarkus Armbruster     range_set_bounds(r, s, e);
997c47959dSEric Blake     sov->ranges = range_list_insert(sov->ranges, r);
10069e25563SHu Tao }
10169e25563SHu Tao 
10269e25563SHu Tao static void format_string(StringOutputVisitor *sov, Range *r, bool next,
10369e25563SHu Tao                           bool human)
10469e25563SHu Tao {
105a0efbf16SMarkus Armbruster     if (range_lob(r) != range_upb(r)) {
10669e25563SHu Tao         if (human) {
107684531adSHu Tao             g_string_append_printf(sov->string, "0x%" PRIx64 "-0x%" PRIx64,
108a0efbf16SMarkus Armbruster                                    range_lob(r), range_upb(r));
10969e25563SHu Tao 
11069e25563SHu Tao         } else {
11169e25563SHu Tao             g_string_append_printf(sov->string, "%" PRId64 "-%" PRId64,
112a0efbf16SMarkus Armbruster                                    range_lob(r), range_upb(r));
11369e25563SHu Tao         }
11469e25563SHu Tao     } else {
11569e25563SHu Tao         if (human) {
116a0efbf16SMarkus Armbruster             g_string_append_printf(sov->string, "0x%" PRIx64, range_lob(r));
11769e25563SHu Tao         } else {
118a0efbf16SMarkus Armbruster             g_string_append_printf(sov->string, "%" PRId64, range_lob(r));
11969e25563SHu Tao         }
12069e25563SHu Tao     }
12169e25563SHu Tao     if (next) {
12269e25563SHu Tao         g_string_append(sov->string, ",");
12369e25563SHu Tao     }
124a020f980SPaolo Bonzini }
125a020f980SPaolo Bonzini 
1260b2a0d6bSEric Blake static void print_type_int64(Visitor *v, const char *name, int64_t *obj,
127a020f980SPaolo Bonzini                              Error **errp)
128a020f980SPaolo Bonzini {
129d7bea75dSEric Blake     StringOutputVisitor *sov = to_sov(v);
13069e25563SHu Tao     GList *l;
13169e25563SHu Tao 
13269e25563SHu Tao     switch (sov->list_mode) {
13369e25563SHu Tao     case LM_NONE:
13469e25563SHu Tao         string_output_append(sov, *obj);
13569e25563SHu Tao         break;
13669e25563SHu Tao 
13769e25563SHu Tao     case LM_STARTED:
13869e25563SHu Tao         sov->range_start.s = *obj;
13969e25563SHu Tao         sov->range_end.s = *obj;
14069e25563SHu Tao         sov->list_mode = LM_IN_PROGRESS;
14169e25563SHu Tao         return;
14269e25563SHu Tao 
14369e25563SHu Tao     case LM_IN_PROGRESS:
14469e25563SHu Tao         if (sov->range_end.s + 1 == *obj) {
14569e25563SHu Tao             sov->range_end.s++;
14669e25563SHu Tao         } else {
14769e25563SHu Tao             if (sov->range_start.s == sov->range_end.s) {
14869e25563SHu Tao                 string_output_append(sov, sov->range_end.s);
14969e25563SHu Tao             } else {
15069e25563SHu Tao                 assert(sov->range_start.s < sov->range_end.s);
15169e25563SHu Tao                 string_output_append_range(sov, sov->range_start.s,
15269e25563SHu Tao                                            sov->range_end.s);
15369e25563SHu Tao             }
15469e25563SHu Tao 
15569e25563SHu Tao             sov->range_start.s = *obj;
15669e25563SHu Tao             sov->range_end.s = *obj;
15769e25563SHu Tao         }
15869e25563SHu Tao         return;
15969e25563SHu Tao 
16069e25563SHu Tao     case LM_END:
16169e25563SHu Tao         if (sov->range_end.s + 1 == *obj) {
16269e25563SHu Tao             sov->range_end.s++;
16369e25563SHu Tao             assert(sov->range_start.s < sov->range_end.s);
16469e25563SHu Tao             string_output_append_range(sov, sov->range_start.s,
16569e25563SHu Tao                                        sov->range_end.s);
16669e25563SHu Tao         } else {
16769e25563SHu Tao             if (sov->range_start.s == sov->range_end.s) {
16869e25563SHu Tao                 string_output_append(sov, sov->range_end.s);
16969e25563SHu Tao             } else {
17069e25563SHu Tao                 assert(sov->range_start.s < sov->range_end.s);
17169e25563SHu Tao 
17269e25563SHu Tao                 string_output_append_range(sov, sov->range_start.s,
17369e25563SHu Tao                                            sov->range_end.s);
17469e25563SHu Tao             }
17569e25563SHu Tao             string_output_append(sov, *obj);
17669e25563SHu Tao         }
17769e25563SHu Tao         break;
17869e25563SHu Tao 
17969e25563SHu Tao     default:
18069e25563SHu Tao         abort();
18169e25563SHu Tao     }
18269e25563SHu Tao 
18369e25563SHu Tao     l = sov->ranges;
18469e25563SHu Tao     while (l) {
18569e25563SHu Tao         Range *r = l->data;
18669e25563SHu Tao         format_string(sov, r, l->next != NULL, false);
18769e25563SHu Tao         l = l->next;
18869e25563SHu Tao     }
1890b7593e0SPaolo Bonzini 
1900b7593e0SPaolo Bonzini     if (sov->human) {
19169e25563SHu Tao         l = sov->ranges;
19269e25563SHu Tao         g_string_append(sov->string, " (");
19369e25563SHu Tao         while (l) {
19469e25563SHu Tao             Range *r = l->data;
19556fdfb61SMichael S. Tsirkin             format_string(sov, r, l->next != NULL, true);
19669e25563SHu Tao             l = l->next;
1970b7593e0SPaolo Bonzini         }
19869e25563SHu Tao         g_string_append(sov->string, ")");
19969e25563SHu Tao     }
2000b7593e0SPaolo Bonzini }
2010b7593e0SPaolo Bonzini 
2020b2a0d6bSEric Blake static void print_type_uint64(Visitor *v, const char *name, uint64_t *obj,
203f755dea7SEric Blake                              Error **errp)
204f755dea7SEric Blake {
205f755dea7SEric Blake     /* FIXME: print_type_int64 mishandles values over INT64_MAX */
206f755dea7SEric Blake     int64_t i = *obj;
2070b2a0d6bSEric Blake     print_type_int64(v, name, &i, errp);
208f755dea7SEric Blake }
209f755dea7SEric Blake 
2100b2a0d6bSEric Blake static void print_type_size(Visitor *v, const char *name, uint64_t *obj,
2110b7593e0SPaolo Bonzini                             Error **errp)
2120b7593e0SPaolo Bonzini {
213d7bea75dSEric Blake     StringOutputVisitor *sov = to_sov(v);
21422951aaaSPeter Xu     uint64_t val;
21522951aaaSPeter Xu     char *out, *psize;
2160b7593e0SPaolo Bonzini 
2170b7593e0SPaolo Bonzini     if (!sov->human) {
218e41b509dSPaolo Bonzini         out = g_strdup_printf("%"PRIu64, *obj);
2190b7593e0SPaolo Bonzini         string_output_set(sov, out);
2200b7593e0SPaolo Bonzini         return;
2210b7593e0SPaolo Bonzini     }
2220b7593e0SPaolo Bonzini 
2230b7593e0SPaolo Bonzini     val = *obj;
22422951aaaSPeter Xu     psize = size_to_str(val);
22522951aaaSPeter Xu     out = g_strdup_printf("%"PRIu64" (%s)", val, psize);
2260b7593e0SPaolo Bonzini     string_output_set(sov, out);
22722951aaaSPeter Xu 
22822951aaaSPeter Xu     g_free(psize);
229a020f980SPaolo Bonzini }
230a020f980SPaolo Bonzini 
2310b2a0d6bSEric Blake static void print_type_bool(Visitor *v, const char *name, bool *obj,
232a020f980SPaolo Bonzini                             Error **errp)
233a020f980SPaolo Bonzini {
234d7bea75dSEric Blake     StringOutputVisitor *sov = to_sov(v);
235a020f980SPaolo Bonzini     string_output_set(sov, g_strdup(*obj ? "true" : "false"));
236a020f980SPaolo Bonzini }
237a020f980SPaolo Bonzini 
2380b2a0d6bSEric Blake static void print_type_str(Visitor *v, const char *name, char **obj,
239a020f980SPaolo Bonzini                            Error **errp)
240a020f980SPaolo Bonzini {
241d7bea75dSEric Blake     StringOutputVisitor *sov = to_sov(v);
2420b7593e0SPaolo Bonzini     char *out;
2430b7593e0SPaolo Bonzini 
2440b7593e0SPaolo Bonzini     if (sov->human) {
2450b7593e0SPaolo Bonzini         out = *obj ? g_strdup_printf("\"%s\"", *obj) : g_strdup("<null>");
2460b7593e0SPaolo Bonzini     } else {
2470b7593e0SPaolo Bonzini         out = g_strdup(*obj ? *obj : "");
2480b7593e0SPaolo Bonzini     }
2490b7593e0SPaolo Bonzini     string_output_set(sov, out);
250a020f980SPaolo Bonzini }
251a020f980SPaolo Bonzini 
2520b2a0d6bSEric Blake static void print_type_number(Visitor *v, const char *name, double *obj,
253a020f980SPaolo Bonzini                               Error **errp)
254a020f980SPaolo Bonzini {
255d7bea75dSEric Blake     StringOutputVisitor *sov = to_sov(v);
256173bbb75SMichael Roth     string_output_set(sov, g_strdup_printf("%f", *obj));
257a020f980SPaolo Bonzini }
258a020f980SPaolo Bonzini 
259d2f95f4dSMarkus Armbruster static void print_type_null(Visitor *v, const char *name, QNull **obj,
260d2f95f4dSMarkus Armbruster                             Error **errp)
261a7333712SGreg Kurz {
262a7333712SGreg Kurz     StringOutputVisitor *sov = to_sov(v);
263a7333712SGreg Kurz     char *out;
264a7333712SGreg Kurz 
265a7333712SGreg Kurz     if (sov->human) {
266a7333712SGreg Kurz         out = g_strdup("<null>");
267a7333712SGreg Kurz     } else {
268a7333712SGreg Kurz         out = g_strdup("");
269a7333712SGreg Kurz     }
270a7333712SGreg Kurz     string_output_set(sov, out);
271a7333712SGreg Kurz }
272a7333712SGreg Kurz 
27369e25563SHu Tao static void
274d9f62ddeSEric Blake start_list(Visitor *v, const char *name, GenericList **list, size_t size,
275d9f62ddeSEric Blake            Error **errp)
27669e25563SHu Tao {
277d7bea75dSEric Blake     StringOutputVisitor *sov = to_sov(v);
27869e25563SHu Tao 
27969e25563SHu Tao     /* we can't traverse a list in a list */
28069e25563SHu Tao     assert(sov->list_mode == LM_NONE);
281d9f62ddeSEric Blake     /* We don't support visits without a list */
282d9f62ddeSEric Blake     assert(list);
2831158bb2aSEric Blake     sov->list = list;
284d9f62ddeSEric Blake     /* List handling is only needed if there are at least two elements */
285d9f62ddeSEric Blake     if (*list && (*list)->next) {
28669e25563SHu Tao         sov->list_mode = LM_STARTED;
287d9f62ddeSEric Blake     }
28869e25563SHu Tao }
28969e25563SHu Tao 
290d9f62ddeSEric Blake static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
29169e25563SHu Tao {
292d7bea75dSEric Blake     StringOutputVisitor *sov = to_sov(v);
293d9f62ddeSEric Blake     GenericList *ret = tail->next;
29469e25563SHu Tao 
295d9f62ddeSEric Blake     if (ret && !ret->next) {
29669e25563SHu Tao         sov->list_mode = LM_END;
29769e25563SHu Tao     }
29869e25563SHu Tao     return ret;
29969e25563SHu Tao }
30069e25563SHu Tao 
3011158bb2aSEric Blake static void end_list(Visitor *v, void **obj)
30269e25563SHu Tao {
303d7bea75dSEric Blake     StringOutputVisitor *sov = to_sov(v);
30469e25563SHu Tao 
3051158bb2aSEric Blake     assert(sov->list == obj);
30669e25563SHu Tao     assert(sov->list_mode == LM_STARTED ||
30769e25563SHu Tao            sov->list_mode == LM_END ||
30869e25563SHu Tao            sov->list_mode == LM_NONE ||
30969e25563SHu Tao            sov->list_mode == LM_IN_PROGRESS);
31069e25563SHu Tao     sov->list_mode = LM_NONE;
31169e25563SHu Tao }
31269e25563SHu Tao 
3133b098d56SEric Blake static void string_output_complete(Visitor *v, void *opaque)
314a020f980SPaolo Bonzini {
3153b098d56SEric Blake     StringOutputVisitor *sov = to_sov(v);
316a020f980SPaolo Bonzini 
3173b098d56SEric Blake     assert(opaque == sov->result);
3183b098d56SEric Blake     *sov->result = g_string_free(sov->string, false);
3193b098d56SEric Blake     sov->string = NULL;
320a020f980SPaolo Bonzini }
321a020f980SPaolo Bonzini 
3220d156683SMichael S. Tsirkin static void free_range(void *range, void *dummy)
3230d156683SMichael S. Tsirkin {
3240d156683SMichael S. Tsirkin     g_free(range);
3250d156683SMichael S. Tsirkin }
3260d156683SMichael S. Tsirkin 
3272c0ef9f4SEric Blake static void string_output_free(Visitor *v)
3282c0ef9f4SEric Blake {
3292c0ef9f4SEric Blake     StringOutputVisitor *sov = to_sov(v);
3302c0ef9f4SEric Blake 
33169e25563SHu Tao     if (sov->string) {
33269e25563SHu Tao         g_string_free(sov->string, true);
33369e25563SHu Tao     }
33469e25563SHu Tao 
3350d156683SMichael S. Tsirkin     g_list_foreach(sov->ranges, free_range, NULL);
3360d156683SMichael S. Tsirkin     g_list_free(sov->ranges);
337a020f980SPaolo Bonzini     g_free(sov);
338a020f980SPaolo Bonzini }
339a020f980SPaolo Bonzini 
3403b098d56SEric Blake Visitor *string_output_visitor_new(bool human, char **result)
341a020f980SPaolo Bonzini {
342a020f980SPaolo Bonzini     StringOutputVisitor *v;
343a020f980SPaolo Bonzini 
344a020f980SPaolo Bonzini     v = g_malloc0(sizeof(*v));
345a020f980SPaolo Bonzini 
34669e25563SHu Tao     v->string = g_string_new(NULL);
3470b7593e0SPaolo Bonzini     v->human = human;
3483b098d56SEric Blake     v->result = result;
3493b098d56SEric Blake     *result = NULL;
3503b098d56SEric Blake 
351983f52d4SEric Blake     v->visitor.type = VISITOR_OUTPUT;
3524c40314aSEric Blake     v->visitor.type_int64 = print_type_int64;
353f755dea7SEric Blake     v->visitor.type_uint64 = print_type_uint64;
3540b7593e0SPaolo Bonzini     v->visitor.type_size = print_type_size;
355a020f980SPaolo Bonzini     v->visitor.type_bool = print_type_bool;
356a020f980SPaolo Bonzini     v->visitor.type_str = print_type_str;
357a020f980SPaolo Bonzini     v->visitor.type_number = print_type_number;
358a7333712SGreg Kurz     v->visitor.type_null = print_type_null;
35969e25563SHu Tao     v->visitor.start_list = start_list;
36069e25563SHu Tao     v->visitor.next_list = next_list;
36169e25563SHu Tao     v->visitor.end_list = end_list;
3623b098d56SEric Blake     v->visitor.complete = string_output_complete;
3632c0ef9f4SEric Blake     v->visitor.free = string_output_free;
364a020f980SPaolo Bonzini 
3653b098d56SEric Blake     return &v->visitor;
366a020f980SPaolo Bonzini }
367