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