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" 14a020f980SPaolo Bonzini #include "qemu-common.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 259*a7333712SGreg Kurz static void print_type_null(Visitor *v, const char *name, Error **errp) 260*a7333712SGreg Kurz { 261*a7333712SGreg Kurz StringOutputVisitor *sov = to_sov(v); 262*a7333712SGreg Kurz char *out; 263*a7333712SGreg Kurz 264*a7333712SGreg Kurz if (sov->human) { 265*a7333712SGreg Kurz out = g_strdup("<null>"); 266*a7333712SGreg Kurz } else { 267*a7333712SGreg Kurz out = g_strdup(""); 268*a7333712SGreg Kurz } 269*a7333712SGreg Kurz string_output_set(sov, out); 270*a7333712SGreg Kurz } 271*a7333712SGreg Kurz 27269e25563SHu Tao static void 273d9f62ddeSEric Blake start_list(Visitor *v, const char *name, GenericList **list, size_t size, 274d9f62ddeSEric Blake Error **errp) 27569e25563SHu Tao { 276d7bea75dSEric Blake StringOutputVisitor *sov = to_sov(v); 27769e25563SHu Tao 27869e25563SHu Tao /* we can't traverse a list in a list */ 27969e25563SHu Tao assert(sov->list_mode == LM_NONE); 280d9f62ddeSEric Blake /* We don't support visits without a list */ 281d9f62ddeSEric Blake assert(list); 2821158bb2aSEric Blake sov->list = list; 283d9f62ddeSEric Blake /* List handling is only needed if there are at least two elements */ 284d9f62ddeSEric Blake if (*list && (*list)->next) { 28569e25563SHu Tao sov->list_mode = LM_STARTED; 286d9f62ddeSEric Blake } 28769e25563SHu Tao } 28869e25563SHu Tao 289d9f62ddeSEric Blake static GenericList *next_list(Visitor *v, GenericList *tail, size_t size) 29069e25563SHu Tao { 291d7bea75dSEric Blake StringOutputVisitor *sov = to_sov(v); 292d9f62ddeSEric Blake GenericList *ret = tail->next; 29369e25563SHu Tao 294d9f62ddeSEric Blake if (ret && !ret->next) { 29569e25563SHu Tao sov->list_mode = LM_END; 29669e25563SHu Tao } 29769e25563SHu Tao return ret; 29869e25563SHu Tao } 29969e25563SHu Tao 3001158bb2aSEric Blake static void end_list(Visitor *v, void **obj) 30169e25563SHu Tao { 302d7bea75dSEric Blake StringOutputVisitor *sov = to_sov(v); 30369e25563SHu Tao 3041158bb2aSEric Blake assert(sov->list == obj); 30569e25563SHu Tao assert(sov->list_mode == LM_STARTED || 30669e25563SHu Tao sov->list_mode == LM_END || 30769e25563SHu Tao sov->list_mode == LM_NONE || 30869e25563SHu Tao sov->list_mode == LM_IN_PROGRESS); 30969e25563SHu Tao sov->list_mode = LM_NONE; 31069e25563SHu Tao } 31169e25563SHu Tao 3123b098d56SEric Blake static void string_output_complete(Visitor *v, void *opaque) 313a020f980SPaolo Bonzini { 3143b098d56SEric Blake StringOutputVisitor *sov = to_sov(v); 315a020f980SPaolo Bonzini 3163b098d56SEric Blake assert(opaque == sov->result); 3173b098d56SEric Blake *sov->result = g_string_free(sov->string, false); 3183b098d56SEric Blake sov->string = NULL; 319a020f980SPaolo Bonzini } 320a020f980SPaolo Bonzini 3210d156683SMichael S. Tsirkin static void free_range(void *range, void *dummy) 3220d156683SMichael S. Tsirkin { 3230d156683SMichael S. Tsirkin g_free(range); 3240d156683SMichael S. Tsirkin } 3250d156683SMichael S. Tsirkin 3262c0ef9f4SEric Blake static void string_output_free(Visitor *v) 3272c0ef9f4SEric Blake { 3282c0ef9f4SEric Blake StringOutputVisitor *sov = to_sov(v); 3292c0ef9f4SEric Blake 33069e25563SHu Tao if (sov->string) { 33169e25563SHu Tao g_string_free(sov->string, true); 33269e25563SHu Tao } 33369e25563SHu Tao 3340d156683SMichael S. Tsirkin g_list_foreach(sov->ranges, free_range, NULL); 3350d156683SMichael S. Tsirkin g_list_free(sov->ranges); 336a020f980SPaolo Bonzini g_free(sov); 337a020f980SPaolo Bonzini } 338a020f980SPaolo Bonzini 3393b098d56SEric Blake Visitor *string_output_visitor_new(bool human, char **result) 340a020f980SPaolo Bonzini { 341a020f980SPaolo Bonzini StringOutputVisitor *v; 342a020f980SPaolo Bonzini 343a020f980SPaolo Bonzini v = g_malloc0(sizeof(*v)); 344a020f980SPaolo Bonzini 34569e25563SHu Tao v->string = g_string_new(NULL); 3460b7593e0SPaolo Bonzini v->human = human; 3473b098d56SEric Blake v->result = result; 3483b098d56SEric Blake *result = NULL; 3493b098d56SEric Blake 350983f52d4SEric Blake v->visitor.type = VISITOR_OUTPUT; 3514c40314aSEric Blake v->visitor.type_int64 = print_type_int64; 352f755dea7SEric Blake v->visitor.type_uint64 = print_type_uint64; 3530b7593e0SPaolo Bonzini v->visitor.type_size = print_type_size; 354a020f980SPaolo Bonzini v->visitor.type_bool = print_type_bool; 355a020f980SPaolo Bonzini v->visitor.type_str = print_type_str; 356a020f980SPaolo Bonzini v->visitor.type_number = print_type_number; 357*a7333712SGreg Kurz v->visitor.type_null = print_type_null; 35869e25563SHu Tao v->visitor.start_list = start_list; 35969e25563SHu Tao v->visitor.next_list = next_list; 36069e25563SHu Tao v->visitor.end_list = end_list; 3613b098d56SEric Blake v->visitor.complete = string_output_complete; 3622c0ef9f4SEric Blake v->visitor.free = string_output_free; 363a020f980SPaolo Bonzini 3643b098d56SEric Blake return &v->visitor; 365a020f980SPaolo Bonzini } 366