xref: /qemu/qapi/string-output-visitor.c (revision 4bda8224)
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 start_struct(Visitor *v, const char *name, void **obj,
296                          size_t size, Error **errp)
297 {
298     return true;
299 }
300 
301 static void end_struct(Visitor *v, void **obj)
302 {
303     StringOutputVisitor *sov = to_sov(v);
304 
305     /* TODO actually print struct fields */
306     string_output_set(sov, g_strdup("<omitted>"));
307 }
308 
309 static bool
310 start_list(Visitor *v, const char *name, GenericList **list, size_t size,
311            Error **errp)
312 {
313     StringOutputVisitor *sov = to_sov(v);
314 
315     /* we can't traverse a list in a list */
316     assert(sov->list_mode == LM_NONE);
317     /* We don't support visits without a list */
318     assert(list);
319     sov->list = list;
320     /* List handling is only needed if there are at least two elements */
321     if (*list && (*list)->next) {
322         sov->list_mode = LM_STARTED;
323     }
324     return true;
325 }
326 
327 static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
328 {
329     StringOutputVisitor *sov = to_sov(v);
330     GenericList *ret = tail->next;
331 
332     if (ret && !ret->next) {
333         sov->list_mode = LM_END;
334     }
335     return ret;
336 }
337 
338 static void end_list(Visitor *v, void **obj)
339 {
340     StringOutputVisitor *sov = to_sov(v);
341 
342     assert(sov->list == obj);
343     assert(sov->list_mode == LM_STARTED ||
344            sov->list_mode == LM_END ||
345            sov->list_mode == LM_NONE ||
346            sov->list_mode == LM_IN_PROGRESS);
347     sov->list_mode = LM_NONE;
348 }
349 
350 static void string_output_complete(Visitor *v, void *opaque)
351 {
352     StringOutputVisitor *sov = to_sov(v);
353 
354     assert(opaque == sov->result);
355     *sov->result = g_string_free(sov->string, false);
356     sov->string = NULL;
357 }
358 
359 static void free_range(void *range, void *dummy)
360 {
361     g_free(range);
362 }
363 
364 static void string_output_free(Visitor *v)
365 {
366     StringOutputVisitor *sov = to_sov(v);
367 
368     if (sov->string) {
369         g_string_free(sov->string, true);
370     }
371 
372     g_list_foreach(sov->ranges, free_range, NULL);
373     g_list_free(sov->ranges);
374     g_free(sov);
375 }
376 
377 Visitor *string_output_visitor_new(bool human, char **result)
378 {
379     StringOutputVisitor *v;
380 
381     v = g_malloc0(sizeof(*v));
382 
383     v->string = g_string_new(NULL);
384     v->human = human;
385     v->result = result;
386     *result = NULL;
387 
388     v->visitor.type = VISITOR_OUTPUT;
389     v->visitor.type_int64 = print_type_int64;
390     v->visitor.type_uint64 = print_type_uint64;
391     v->visitor.type_size = print_type_size;
392     v->visitor.type_bool = print_type_bool;
393     v->visitor.type_str = print_type_str;
394     v->visitor.type_number = print_type_number;
395     v->visitor.type_null = print_type_null;
396     v->visitor.start_struct = start_struct;
397     v->visitor.end_struct = end_struct;
398     v->visitor.start_list = start_list;
399     v->visitor.next_list = next_list;
400     v->visitor.end_list = end_list;
401     v->visitor.complete = string_output_complete;
402     v->visitor.free = string_output_free;
403 
404     return &v->visitor;
405 }
406