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