xref: /qemu/qapi/string-output-visitor.c (revision efade66d)
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     switch (sov->list_mode) {
78     case LM_STARTED:
79         sov->list_mode = LM_IN_PROGRESS;
80         /* fall through */
81     case LM_NONE:
82         if (sov->string) {
83             g_string_free(sov->string, true);
84         }
85         sov->string = g_string_new(string);
86         g_free(string);
87         break;
88 
89     case LM_IN_PROGRESS:
90     case LM_END:
91         g_string_append(sov->string, ", ");
92         g_string_append(sov->string, string);
93         break;
94 
95     default:
96         abort();
97     }
98 }
99 
100 static void string_output_append(StringOutputVisitor *sov, int64_t a)
101 {
102     Range *r = g_malloc0(sizeof(*r));
103 
104     range_set_bounds(r, a, a);
105     sov->ranges = range_list_insert(sov->ranges, r);
106 }
107 
108 static void string_output_append_range(StringOutputVisitor *sov,
109                                        int64_t s, int64_t e)
110 {
111     Range *r = g_malloc0(sizeof(*r));
112 
113     range_set_bounds(r, s, e);
114     sov->ranges = range_list_insert(sov->ranges, r);
115 }
116 
117 static void format_string(StringOutputVisitor *sov, Range *r, bool next,
118                           bool human)
119 {
120     if (range_lob(r) != range_upb(r)) {
121         if (human) {
122             g_string_append_printf(sov->string, "0x%" PRIx64 "-0x%" PRIx64,
123                                    range_lob(r), range_upb(r));
124 
125         } else {
126             g_string_append_printf(sov->string, "%" PRId64 "-%" PRId64,
127                                    range_lob(r), range_upb(r));
128         }
129     } else {
130         if (human) {
131             g_string_append_printf(sov->string, "0x%" PRIx64, range_lob(r));
132         } else {
133             g_string_append_printf(sov->string, "%" PRId64, range_lob(r));
134         }
135     }
136     if (next) {
137         g_string_append(sov->string, ",");
138     }
139 }
140 
141 static bool print_type_int64(Visitor *v, const char *name, int64_t *obj,
142                              Error **errp)
143 {
144     StringOutputVisitor *sov = to_sov(v);
145     GList *l;
146 
147     switch (sov->list_mode) {
148     case LM_NONE:
149         string_output_append(sov, *obj);
150         break;
151 
152     case LM_STARTED:
153         sov->range_start.s = *obj;
154         sov->range_end.s = *obj;
155         sov->list_mode = LM_IN_PROGRESS;
156         return true;
157 
158     case LM_IN_PROGRESS:
159         if (sov->range_end.s + 1 == *obj) {
160             sov->range_end.s++;
161         } else {
162             if (sov->range_start.s == sov->range_end.s) {
163                 string_output_append(sov, sov->range_end.s);
164             } else {
165                 assert(sov->range_start.s < sov->range_end.s);
166                 string_output_append_range(sov, sov->range_start.s,
167                                            sov->range_end.s);
168             }
169 
170             sov->range_start.s = *obj;
171             sov->range_end.s = *obj;
172         }
173         return true;
174 
175     case LM_END:
176         if (sov->range_end.s + 1 == *obj) {
177             sov->range_end.s++;
178             assert(sov->range_start.s < sov->range_end.s);
179             string_output_append_range(sov, sov->range_start.s,
180                                        sov->range_end.s);
181         } else {
182             if (sov->range_start.s == sov->range_end.s) {
183                 string_output_append(sov, sov->range_end.s);
184             } else {
185                 assert(sov->range_start.s < sov->range_end.s);
186 
187                 string_output_append_range(sov, sov->range_start.s,
188                                            sov->range_end.s);
189             }
190             string_output_append(sov, *obj);
191         }
192         break;
193 
194     default:
195         abort();
196     }
197 
198     l = sov->ranges;
199     while (l) {
200         Range *r = l->data;
201         format_string(sov, r, l->next != NULL, false);
202         l = l->next;
203     }
204 
205     if (sov->human) {
206         l = sov->ranges;
207         g_string_append(sov->string, " (");
208         while (l) {
209             Range *r = l->data;
210             format_string(sov, r, l->next != NULL, true);
211             l = l->next;
212         }
213         g_string_append(sov->string, ")");
214     }
215 
216     return true;
217 }
218 
219 static bool print_type_uint64(Visitor *v, const char *name, uint64_t *obj,
220                              Error **errp)
221 {
222     /* FIXME: print_type_int64 mishandles values over INT64_MAX */
223     int64_t i = *obj;
224     return print_type_int64(v, name, &i, errp);
225 }
226 
227 static bool print_type_size(Visitor *v, const char *name, uint64_t *obj,
228                             Error **errp)
229 {
230     StringOutputVisitor *sov = to_sov(v);
231     uint64_t val;
232     char *out, *psize;
233 
234     if (!sov->human) {
235         out = g_strdup_printf("%"PRIu64, *obj);
236         string_output_set(sov, out);
237         return true;
238     }
239 
240     val = *obj;
241     psize = size_to_str(val);
242     out = g_strdup_printf("%"PRIu64" (%s)", val, psize);
243     string_output_set(sov, out);
244 
245     g_free(psize);
246     return true;
247 }
248 
249 static bool print_type_bool(Visitor *v, const char *name, bool *obj,
250                             Error **errp)
251 {
252     StringOutputVisitor *sov = to_sov(v);
253     string_output_set(sov, g_strdup(*obj ? "true" : "false"));
254     return true;
255 }
256 
257 static bool print_type_str(Visitor *v, const char *name, char **obj,
258                            Error **errp)
259 {
260     StringOutputVisitor *sov = to_sov(v);
261     char *out;
262 
263     if (sov->human) {
264         out = *obj ? g_strdup_printf("\"%s\"", *obj) : g_strdup("<null>");
265     } else {
266         out = g_strdup(*obj ? *obj : "");
267     }
268     string_output_set(sov, out);
269     return true;
270 }
271 
272 static bool print_type_number(Visitor *v, const char *name, double *obj,
273                               Error **errp)
274 {
275     StringOutputVisitor *sov = to_sov(v);
276     string_output_set(sov, g_strdup_printf("%.17g", *obj));
277     return true;
278 }
279 
280 static bool print_type_null(Visitor *v, const char *name, QNull **obj,
281                             Error **errp)
282 {
283     StringOutputVisitor *sov = to_sov(v);
284     char *out;
285 
286     if (sov->human) {
287         out = g_strdup("<null>");
288     } else {
289         out = g_strdup("");
290     }
291     string_output_set(sov, out);
292     return true;
293 }
294 
295 static bool
296 start_list(Visitor *v, const char *name, GenericList **list, size_t size,
297            Error **errp)
298 {
299     StringOutputVisitor *sov = to_sov(v);
300 
301     /* we can't traverse a list in a list */
302     assert(sov->list_mode == LM_NONE);
303     /* We don't support visits without a list */
304     assert(list);
305     sov->list = list;
306     /* List handling is only needed if there are at least two elements */
307     if (*list && (*list)->next) {
308         sov->list_mode = LM_STARTED;
309     }
310     return true;
311 }
312 
313 static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
314 {
315     StringOutputVisitor *sov = to_sov(v);
316     GenericList *ret = tail->next;
317 
318     if (ret && !ret->next) {
319         sov->list_mode = LM_END;
320     }
321     return ret;
322 }
323 
324 static void end_list(Visitor *v, void **obj)
325 {
326     StringOutputVisitor *sov = to_sov(v);
327 
328     assert(sov->list == obj);
329     assert(sov->list_mode == LM_STARTED ||
330            sov->list_mode == LM_END ||
331            sov->list_mode == LM_NONE ||
332            sov->list_mode == LM_IN_PROGRESS);
333     sov->list_mode = LM_NONE;
334 }
335 
336 static void string_output_complete(Visitor *v, void *opaque)
337 {
338     StringOutputVisitor *sov = to_sov(v);
339 
340     assert(opaque == sov->result);
341     *sov->result = g_string_free(sov->string, false);
342     sov->string = NULL;
343 }
344 
345 static void free_range(void *range, void *dummy)
346 {
347     g_free(range);
348 }
349 
350 static void string_output_free(Visitor *v)
351 {
352     StringOutputVisitor *sov = to_sov(v);
353 
354     if (sov->string) {
355         g_string_free(sov->string, true);
356     }
357 
358     g_list_foreach(sov->ranges, free_range, NULL);
359     g_list_free(sov->ranges);
360     g_free(sov);
361 }
362 
363 Visitor *string_output_visitor_new(bool human, char **result)
364 {
365     StringOutputVisitor *v;
366 
367     v = g_malloc0(sizeof(*v));
368 
369     v->string = g_string_new(NULL);
370     v->human = human;
371     v->result = result;
372     *result = NULL;
373 
374     v->visitor.type = VISITOR_OUTPUT;
375     v->visitor.type_int64 = print_type_int64;
376     v->visitor.type_uint64 = print_type_uint64;
377     v->visitor.type_size = print_type_size;
378     v->visitor.type_bool = print_type_bool;
379     v->visitor.type_str = print_type_str;
380     v->visitor.type_number = print_type_number;
381     v->visitor.type_null = print_type_null;
382     v->visitor.start_list = start_list;
383     v->visitor.next_list = next_list;
384     v->visitor.end_list = end_list;
385     v->visitor.complete = string_output_complete;
386     v->visitor.free = string_output_free;
387 
388     return &v->visitor;
389 }
390