]> git.proxmox.com Git - mirror_qemu.git/blob - qapi/string-output-visitor.c
docs/interop/bitmaps: Clean up a reference to qemu-qmp-ref
[mirror_qemu.git] / qapi / string-output-visitor.c
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 }