xref: /qemu/qapi/string-output-visitor.c (revision c4b8ffcb)
1 /*
2  * String printing Visitor
3  *
4  * Copyright Red Hat, Inc. 2012-2016
5  *
6  * Author: Paolo Bonzini <pbonzini@redhat.com>
7  *
8  * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
9  * See the COPYING.LIB file in the top-level directory.
10  *
11  */
12 
13 #include "qemu/osdep.h"
14 #include "qemu/cutils.h"
15 #include "qapi/string-output-visitor.h"
16 #include "qapi/visitor-impl.h"
17 #include <math.h>
18 #include "qemu/range.h"
19 
20 enum ListMode {
21     LM_NONE,             /* not traversing a list of repeated options */
22     LM_STARTED,          /* next_list() ready to be called */
23 
24     LM_IN_PROGRESS,      /* next_list() has been called.
25                           *
26                           * Generating the next list link will consume the most
27                           * recently parsed QemuOpt instance of the repeated
28                           * option.
29                           *
30                           * Parsing a value into the list link will examine the
31                           * next QemuOpt instance of the repeated option, and
32                           * possibly enter LM_SIGNED_INTERVAL or
33                           * LM_UNSIGNED_INTERVAL.
34                           */
35 
36     LM_SIGNED_INTERVAL,  /* next_list() has been called.
37                           *
38                           * Generating the next list link will consume the most
39                           * recently stored element from the signed interval,
40                           * parsed from the most recent QemuOpt instance of the
41                           * repeated option. This may consume QemuOpt itself
42                           * and return to LM_IN_PROGRESS.
43                           *
44                           * Parsing a value into the list link will store the
45                           * next element of the signed interval.
46                           */
47 
48     LM_UNSIGNED_INTERVAL,/* Same as above, only for an unsigned interval. */
49 
50     LM_END,              /* next_list() called, about to see last element. */
51 };
52 
53 typedef enum ListMode ListMode;
54 
55 struct StringOutputVisitor
56 {
57     Visitor visitor;
58     bool human;
59     GString *string;
60     char **result;
61     ListMode list_mode;
62     union {
63         int64_t s;
64         uint64_t u;
65     } range_start, range_end;
66     GList *ranges;
67     void *list; /* Only needed for sanity checking the caller */
68 };
69 
70 static StringOutputVisitor *to_sov(Visitor *v)
71 {
72     return container_of(v, StringOutputVisitor, visitor);
73 }
74 
75 static void string_output_set(StringOutputVisitor *sov, char *string)
76 {
77     if (sov->string) {
78         g_string_free(sov->string, true);
79     }
80     sov->string = g_string_new(string);
81     g_free(string);
82 }
83 
84 static void string_output_append(StringOutputVisitor *sov, int64_t a)
85 {
86     Range *r = g_malloc0(sizeof(*r));
87 
88     range_set_bounds(r, a, a);
89     sov->ranges = range_list_insert(sov->ranges, r);
90 }
91 
92 static void string_output_append_range(StringOutputVisitor *sov,
93                                        int64_t s, int64_t e)
94 {
95     Range *r = g_malloc0(sizeof(*r));
96 
97     range_set_bounds(r, s, e);
98     sov->ranges = range_list_insert(sov->ranges, r);
99 }
100 
101 static void format_string(StringOutputVisitor *sov, Range *r, bool next,
102                           bool human)
103 {
104     if (range_lob(r) != range_upb(r)) {
105         if (human) {
106             g_string_append_printf(sov->string, "0x%" PRIx64 "-0x%" PRIx64,
107                                    range_lob(r), range_upb(r));
108 
109         } else {
110             g_string_append_printf(sov->string, "%" PRId64 "-%" PRId64,
111                                    range_lob(r), range_upb(r));
112         }
113     } else {
114         if (human) {
115             g_string_append_printf(sov->string, "0x%" PRIx64, range_lob(r));
116         } else {
117             g_string_append_printf(sov->string, "%" PRId64, range_lob(r));
118         }
119     }
120     if (next) {
121         g_string_append(sov->string, ",");
122     }
123 }
124 
125 static bool print_type_int64(Visitor *v, const char *name, int64_t *obj,
126                              Error **errp)
127 {
128     StringOutputVisitor *sov = to_sov(v);
129     GList *l;
130 
131     switch (sov->list_mode) {
132     case LM_NONE:
133         string_output_append(sov, *obj);
134         break;
135 
136     case LM_STARTED:
137         sov->range_start.s = *obj;
138         sov->range_end.s = *obj;
139         sov->list_mode = LM_IN_PROGRESS;
140         return true;
141 
142     case LM_IN_PROGRESS:
143         if (sov->range_end.s + 1 == *obj) {
144             sov->range_end.s++;
145         } else {
146             if (sov->range_start.s == sov->range_end.s) {
147                 string_output_append(sov, sov->range_end.s);
148             } else {
149                 assert(sov->range_start.s < sov->range_end.s);
150                 string_output_append_range(sov, sov->range_start.s,
151                                            sov->range_end.s);
152             }
153 
154             sov->range_start.s = *obj;
155             sov->range_end.s = *obj;
156         }
157         return true;
158 
159     case LM_END:
160         if (sov->range_end.s + 1 == *obj) {
161             sov->range_end.s++;
162             assert(sov->range_start.s < sov->range_end.s);
163             string_output_append_range(sov, sov->range_start.s,
164                                        sov->range_end.s);
165         } else {
166             if (sov->range_start.s == sov->range_end.s) {
167                 string_output_append(sov, sov->range_end.s);
168             } else {
169                 assert(sov->range_start.s < sov->range_end.s);
170 
171                 string_output_append_range(sov, sov->range_start.s,
172                                            sov->range_end.s);
173             }
174             string_output_append(sov, *obj);
175         }
176         break;
177 
178     default:
179         abort();
180     }
181 
182     l = sov->ranges;
183     while (l) {
184         Range *r = l->data;
185         format_string(sov, r, l->next != NULL, false);
186         l = l->next;
187     }
188 
189     if (sov->human) {
190         l = sov->ranges;
191         g_string_append(sov->string, " (");
192         while (l) {
193             Range *r = l->data;
194             format_string(sov, r, l->next != NULL, true);
195             l = l->next;
196         }
197         g_string_append(sov->string, ")");
198     }
199 
200     return true;
201 }
202 
203 static bool print_type_uint64(Visitor *v, const char *name, uint64_t *obj,
204                              Error **errp)
205 {
206     /* FIXME: print_type_int64 mishandles values over INT64_MAX */
207     int64_t i = *obj;
208     return print_type_int64(v, name, &i, errp);
209 }
210 
211 static bool print_type_size(Visitor *v, const char *name, uint64_t *obj,
212                             Error **errp)
213 {
214     StringOutputVisitor *sov = to_sov(v);
215     uint64_t val;
216     char *out, *psize;
217 
218     if (!sov->human) {
219         out = g_strdup_printf("%"PRIu64, *obj);
220         string_output_set(sov, out);
221         return true;
222     }
223 
224     val = *obj;
225     psize = size_to_str(val);
226     out = g_strdup_printf("%"PRIu64" (%s)", val, psize);
227     string_output_set(sov, out);
228 
229     g_free(psize);
230     return true;
231 }
232 
233 static bool print_type_bool(Visitor *v, const char *name, bool *obj,
234                             Error **errp)
235 {
236     StringOutputVisitor *sov = to_sov(v);
237     string_output_set(sov, g_strdup(*obj ? "true" : "false"));
238     return true;
239 }
240 
241 static bool print_type_str(Visitor *v, const char *name, char **obj,
242                            Error **errp)
243 {
244     StringOutputVisitor *sov = to_sov(v);
245     char *out;
246 
247     if (sov->human) {
248         out = *obj ? g_strdup_printf("\"%s\"", *obj) : g_strdup("<null>");
249     } else {
250         out = g_strdup(*obj ? *obj : "");
251     }
252     string_output_set(sov, out);
253     return true;
254 }
255 
256 static bool print_type_number(Visitor *v, const char *name, double *obj,
257                               Error **errp)
258 {
259     StringOutputVisitor *sov = to_sov(v);
260     string_output_set(sov, g_strdup_printf("%.17g", *obj));
261     return true;
262 }
263 
264 static bool print_type_null(Visitor *v, const char *name, QNull **obj,
265                             Error **errp)
266 {
267     StringOutputVisitor *sov = to_sov(v);
268     char *out;
269 
270     if (sov->human) {
271         out = g_strdup("<null>");
272     } else {
273         out = g_strdup("");
274     }
275     string_output_set(sov, out);
276     return true;
277 }
278 
279 static bool
280 start_list(Visitor *v, const char *name, GenericList **list, size_t size,
281            Error **errp)
282 {
283     StringOutputVisitor *sov = to_sov(v);
284 
285     /* we can't traverse a list in a list */
286     assert(sov->list_mode == LM_NONE);
287     /* We don't support visits without a list */
288     assert(list);
289     sov->list = list;
290     /* List handling is only needed if there are at least two elements */
291     if (*list && (*list)->next) {
292         sov->list_mode = LM_STARTED;
293     }
294     return true;
295 }
296 
297 static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
298 {
299     StringOutputVisitor *sov = to_sov(v);
300     GenericList *ret = tail->next;
301 
302     if (ret && !ret->next) {
303         sov->list_mode = LM_END;
304     }
305     return ret;
306 }
307 
308 static void end_list(Visitor *v, void **obj)
309 {
310     StringOutputVisitor *sov = to_sov(v);
311 
312     assert(sov->list == obj);
313     assert(sov->list_mode == LM_STARTED ||
314            sov->list_mode == LM_END ||
315            sov->list_mode == LM_NONE ||
316            sov->list_mode == LM_IN_PROGRESS);
317     sov->list_mode = LM_NONE;
318 }
319 
320 static void string_output_complete(Visitor *v, void *opaque)
321 {
322     StringOutputVisitor *sov = to_sov(v);
323 
324     assert(opaque == sov->result);
325     *sov->result = g_string_free(sov->string, false);
326     sov->string = NULL;
327 }
328 
329 static void free_range(void *range, void *dummy)
330 {
331     g_free(range);
332 }
333 
334 static void string_output_free(Visitor *v)
335 {
336     StringOutputVisitor *sov = to_sov(v);
337 
338     if (sov->string) {
339         g_string_free(sov->string, true);
340     }
341 
342     g_list_foreach(sov->ranges, free_range, NULL);
343     g_list_free(sov->ranges);
344     g_free(sov);
345 }
346 
347 Visitor *string_output_visitor_new(bool human, char **result)
348 {
349     StringOutputVisitor *v;
350 
351     v = g_malloc0(sizeof(*v));
352 
353     v->string = g_string_new(NULL);
354     v->human = human;
355     v->result = result;
356     *result = NULL;
357 
358     v->visitor.type = VISITOR_OUTPUT;
359     v->visitor.type_int64 = print_type_int64;
360     v->visitor.type_uint64 = print_type_uint64;
361     v->visitor.type_size = print_type_size;
362     v->visitor.type_bool = print_type_bool;
363     v->visitor.type_str = print_type_str;
364     v->visitor.type_number = print_type_number;
365     v->visitor.type_null = print_type_null;
366     v->visitor.start_list = start_list;
367     v->visitor.next_list = next_list;
368     v->visitor.end_list = end_list;
369     v->visitor.complete = string_output_complete;
370     v->visitor.free = string_output_free;
371 
372     return &v->visitor;
373 }
374