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 296 start_list(Visitor *v, const char *name, GenericList **list, size_t size, 297 Error **errp) 298 { 299 StringOutputVisitor *sov = to_sov(v); 300 301 /* we can't traverse a list in a list */ 302 assert(sov->list_mode == LM_NONE); 303 /* We don't support visits without a list */ 304 assert(list); 305 sov->list = list; 306 /* List handling is only needed if there are at least two elements */ 307 if (*list && (*list)->next) { 308 sov->list_mode = LM_STARTED; 309 } 310 return true; 311 } 312 313 static GenericList *next_list(Visitor *v, GenericList *tail, size_t size) 314 { 315 StringOutputVisitor *sov = to_sov(v); 316 GenericList *ret = tail->next; 317 318 if (ret && !ret->next) { 319 sov->list_mode = LM_END; 320 } 321 return ret; 322 } 323 324 static void end_list(Visitor *v, void **obj) 325 { 326 StringOutputVisitor *sov = to_sov(v); 327 328 assert(sov->list == obj); 329 assert(sov->list_mode == LM_STARTED || 330 sov->list_mode == LM_END || 331 sov->list_mode == LM_NONE || 332 sov->list_mode == LM_IN_PROGRESS); 333 sov->list_mode = LM_NONE; 334 } 335 336 static void string_output_complete(Visitor *v, void *opaque) 337 { 338 StringOutputVisitor *sov = to_sov(v); 339 340 assert(opaque == sov->result); 341 *sov->result = g_string_free(sov->string, false); 342 sov->string = NULL; 343 } 344 345 static void free_range(void *range, void *dummy) 346 { 347 g_free(range); 348 } 349 350 static void string_output_free(Visitor *v) 351 { 352 StringOutputVisitor *sov = to_sov(v); 353 354 if (sov->string) { 355 g_string_free(sov->string, true); 356 } 357 358 g_list_foreach(sov->ranges, free_range, NULL); 359 g_list_free(sov->ranges); 360 g_free(sov); 361 } 362 363 Visitor *string_output_visitor_new(bool human, char **result) 364 { 365 StringOutputVisitor *v; 366 367 v = g_malloc0(sizeof(*v)); 368 369 v->string = g_string_new(NULL); 370 v->human = human; 371 v->result = result; 372 *result = NULL; 373 374 v->visitor.type = VISITOR_OUTPUT; 375 v->visitor.type_int64 = print_type_int64; 376 v->visitor.type_uint64 = print_type_uint64; 377 v->visitor.type_size = print_type_size; 378 v->visitor.type_bool = print_type_bool; 379 v->visitor.type_str = print_type_str; 380 v->visitor.type_number = print_type_number; 381 v->visitor.type_null = print_type_null; 382 v->visitor.start_list = start_list; 383 v->visitor.next_list = next_list; 384 v->visitor.end_list = end_list; 385 v->visitor.complete = string_output_complete; 386 v->visitor.free = string_output_free; 387 388 return &v->visitor; 389 } 390