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