1eb7ee2cbSLaszlo Ersek /* 2eb7ee2cbSLaszlo Ersek * Options Visitor 3eb7ee2cbSLaszlo Ersek * 408f9541dSEric Blake * Copyright Red Hat, Inc. 2012-2016 5eb7ee2cbSLaszlo Ersek * 6eb7ee2cbSLaszlo Ersek * Author: Laszlo Ersek <lersek@redhat.com> 7eb7ee2cbSLaszlo Ersek * 8eb7ee2cbSLaszlo Ersek * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. 9eb7ee2cbSLaszlo Ersek * See the COPYING.LIB file in the top-level directory. 10eb7ee2cbSLaszlo Ersek * 11eb7ee2cbSLaszlo Ersek */ 12eb7ee2cbSLaszlo Ersek 13cbf21151SPeter Maydell #include "qemu/osdep.h" 14da34e65cSMarkus Armbruster #include "qapi/error.h" 15f348b6d1SVeronia Bahaa #include "qemu/cutils.h" 167b1b5d19SPaolo Bonzini #include "qapi/qmp/qerror.h" 177b1b5d19SPaolo Bonzini #include "qapi/opts-visitor.h" 181de7afc9SPaolo Bonzini #include "qemu/queue.h" 191de7afc9SPaolo Bonzini #include "qemu/option_int.h" 207b1b5d19SPaolo Bonzini #include "qapi/visitor-impl.h" 21eb7ee2cbSLaszlo Ersek 22eb7ee2cbSLaszlo Ersek 23d9570434SLaszlo Ersek enum ListMode 24d9570434SLaszlo Ersek { 25d9570434SLaszlo Ersek LM_NONE, /* not traversing a list of repeated options */ 26d8754f40SLaszlo Ersek 27863f195fSAndrey Shinkevich LM_IN_PROGRESS, /* 28863f195fSAndrey Shinkevich * opts_next_list() ready to be called. 29d8754f40SLaszlo Ersek * 30d8754f40SLaszlo Ersek * Generating the next list link will consume the most 31d8754f40SLaszlo Ersek * recently parsed QemuOpt instance of the repeated 32d8754f40SLaszlo Ersek * option. 33d8754f40SLaszlo Ersek * 34d8754f40SLaszlo Ersek * Parsing a value into the list link will examine the 35d8754f40SLaszlo Ersek * next QemuOpt instance of the repeated option, and 36d8754f40SLaszlo Ersek * possibly enter LM_SIGNED_INTERVAL or 37d8754f40SLaszlo Ersek * LM_UNSIGNED_INTERVAL. 38d8754f40SLaszlo Ersek */ 39d8754f40SLaszlo Ersek 40863f195fSAndrey Shinkevich LM_SIGNED_INTERVAL, /* 41863f195fSAndrey Shinkevich * opts_next_list() has been called. 42d8754f40SLaszlo Ersek * 43d8754f40SLaszlo Ersek * Generating the next list link will consume the most 44d8754f40SLaszlo Ersek * recently stored element from the signed interval, 45d8754f40SLaszlo Ersek * parsed from the most recent QemuOpt instance of the 46d8754f40SLaszlo Ersek * repeated option. This may consume QemuOpt itself 47d8754f40SLaszlo Ersek * and return to LM_IN_PROGRESS. 48d8754f40SLaszlo Ersek * 49d8754f40SLaszlo Ersek * Parsing a value into the list link will store the 50d8754f40SLaszlo Ersek * next element of the signed interval. 51d8754f40SLaszlo Ersek */ 52d8754f40SLaszlo Ersek 53863f195fSAndrey Shinkevich LM_UNSIGNED_INTERVAL, /* Same as above, only for an unsigned interval. */ 54863f195fSAndrey Shinkevich 55863f195fSAndrey Shinkevich LM_TRAVERSED /* 56863f195fSAndrey Shinkevich * opts_next_list() has been called. 57863f195fSAndrey Shinkevich * 58863f195fSAndrey Shinkevich * No more QemuOpt instance in the list. 59863f195fSAndrey Shinkevich * The traversal has been completed. 60863f195fSAndrey Shinkevich */ 61d9570434SLaszlo Ersek }; 62d9570434SLaszlo Ersek 63d9570434SLaszlo Ersek typedef enum ListMode ListMode; 64d9570434SLaszlo Ersek 65eb7ee2cbSLaszlo Ersek struct OptsVisitor 66eb7ee2cbSLaszlo Ersek { 67eb7ee2cbSLaszlo Ersek Visitor visitor; 68eb7ee2cbSLaszlo Ersek 69eb7ee2cbSLaszlo Ersek /* Ownership remains with opts_visitor_new()'s caller. */ 70eb7ee2cbSLaszlo Ersek const QemuOpts *opts_root; 71eb7ee2cbSLaszlo Ersek 72eb7ee2cbSLaszlo Ersek unsigned depth; 73eb7ee2cbSLaszlo Ersek 74eb7ee2cbSLaszlo Ersek /* Non-null iff depth is positive. Each key is a QemuOpt name. Each value 75eb7ee2cbSLaszlo Ersek * is a non-empty GQueue, enumerating all QemuOpt occurrences with that 76eb7ee2cbSLaszlo Ersek * name. */ 77eb7ee2cbSLaszlo Ersek GHashTable *unprocessed_opts; 78eb7ee2cbSLaszlo Ersek 79eb7ee2cbSLaszlo Ersek /* The list currently being traversed with opts_start_list() / 80eb7ee2cbSLaszlo Ersek * opts_next_list(). The list must have a struct element type in the 81eb7ee2cbSLaszlo Ersek * schema, with a single mandatory scalar member. */ 82d9570434SLaszlo Ersek ListMode list_mode; 83eb7ee2cbSLaszlo Ersek GQueue *repeated_opts; 84eb7ee2cbSLaszlo Ersek 85d8754f40SLaszlo Ersek /* When parsing a list of repeating options as integers, values of the form 86d8754f40SLaszlo Ersek * "a-b", representing a closed interval, are allowed. Elements in the 87d8754f40SLaszlo Ersek * range are generated individually. 88d8754f40SLaszlo Ersek */ 89d8754f40SLaszlo Ersek union { 90d8754f40SLaszlo Ersek int64_t s; 91d8754f40SLaszlo Ersek uint64_t u; 92d8754f40SLaszlo Ersek } range_next, range_limit; 93d8754f40SLaszlo Ersek 94eb7ee2cbSLaszlo Ersek /* If "opts_root->id" is set, reinstantiate it as a fake QemuOpt for 95eb7ee2cbSLaszlo Ersek * uniformity. Only its "name" and "str" fields are set. "fake_id_opt" does 96eb7ee2cbSLaszlo Ersek * not survive or escape the OptsVisitor object. 97eb7ee2cbSLaszlo Ersek */ 98eb7ee2cbSLaszlo Ersek QemuOpt *fake_id_opt; 99eb7ee2cbSLaszlo Ersek }; 100eb7ee2cbSLaszlo Ersek 101eb7ee2cbSLaszlo Ersek 102d7bea75dSEric Blake static OptsVisitor *to_ov(Visitor *v) 103d7bea75dSEric Blake { 104d7bea75dSEric Blake return container_of(v, OptsVisitor, visitor); 105d7bea75dSEric Blake } 106d7bea75dSEric Blake 107d7bea75dSEric Blake 108eb7ee2cbSLaszlo Ersek static void 109eb7ee2cbSLaszlo Ersek destroy_list(gpointer list) 110eb7ee2cbSLaszlo Ersek { 111eb7ee2cbSLaszlo Ersek g_queue_free(list); 112eb7ee2cbSLaszlo Ersek } 113eb7ee2cbSLaszlo Ersek 114eb7ee2cbSLaszlo Ersek 115eb7ee2cbSLaszlo Ersek static void 116eb7ee2cbSLaszlo Ersek opts_visitor_insert(GHashTable *unprocessed_opts, const QemuOpt *opt) 117eb7ee2cbSLaszlo Ersek { 118eb7ee2cbSLaszlo Ersek GQueue *list; 119eb7ee2cbSLaszlo Ersek 120eb7ee2cbSLaszlo Ersek list = g_hash_table_lookup(unprocessed_opts, opt->name); 121eb7ee2cbSLaszlo Ersek if (list == NULL) { 122eb7ee2cbSLaszlo Ersek list = g_queue_new(); 123eb7ee2cbSLaszlo Ersek 124eb7ee2cbSLaszlo Ersek /* GHashTable will never try to free the keys -- we supply NULL as 125eb7ee2cbSLaszlo Ersek * "key_destroy_func" in opts_start_struct(). Thus cast away key 126eb7ee2cbSLaszlo Ersek * const-ness in order to suppress gcc's warning. 127eb7ee2cbSLaszlo Ersek */ 128eb7ee2cbSLaszlo Ersek g_hash_table_insert(unprocessed_opts, (gpointer)opt->name, list); 129eb7ee2cbSLaszlo Ersek } 130eb7ee2cbSLaszlo Ersek 131eb7ee2cbSLaszlo Ersek /* Similarly, destroy_list() doesn't call g_queue_free_full(). */ 132eb7ee2cbSLaszlo Ersek g_queue_push_tail(list, (gpointer)opt); 133eb7ee2cbSLaszlo Ersek } 134eb7ee2cbSLaszlo Ersek 135eb7ee2cbSLaszlo Ersek 136*012d4c96SMarkus Armbruster static bool 137337283dfSEric Blake opts_start_struct(Visitor *v, const char *name, void **obj, 1380b2a0d6bSEric Blake size_t size, Error **errp) 139eb7ee2cbSLaszlo Ersek { 140d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 141eb7ee2cbSLaszlo Ersek const QemuOpt *opt; 142eb7ee2cbSLaszlo Ersek 143b7745397SMarkus Armbruster if (obj) { 144e58d695eSEric Blake *obj = g_malloc0(size); 145b7745397SMarkus Armbruster } 146eb7ee2cbSLaszlo Ersek if (ov->depth++ > 0) { 147*012d4c96SMarkus Armbruster return true; 148eb7ee2cbSLaszlo Ersek } 149eb7ee2cbSLaszlo Ersek 150eb7ee2cbSLaszlo Ersek ov->unprocessed_opts = g_hash_table_new_full(&g_str_hash, &g_str_equal, 151eb7ee2cbSLaszlo Ersek NULL, &destroy_list); 152eb7ee2cbSLaszlo Ersek QTAILQ_FOREACH(opt, &ov->opts_root->head, next) { 153eb7ee2cbSLaszlo Ersek /* ensured by qemu-option.c::opts_do_parse() */ 154eb7ee2cbSLaszlo Ersek assert(strcmp(opt->name, "id") != 0); 155eb7ee2cbSLaszlo Ersek 156eb7ee2cbSLaszlo Ersek opts_visitor_insert(ov->unprocessed_opts, opt); 157eb7ee2cbSLaszlo Ersek } 158eb7ee2cbSLaszlo Ersek 159eb7ee2cbSLaszlo Ersek if (ov->opts_root->id != NULL) { 160eb7ee2cbSLaszlo Ersek ov->fake_id_opt = g_malloc0(sizeof *ov->fake_id_opt); 161eb7ee2cbSLaszlo Ersek 162dc8622f2SChunyan Liu ov->fake_id_opt->name = g_strdup("id"); 163dc8622f2SChunyan Liu ov->fake_id_opt->str = g_strdup(ov->opts_root->id); 164eb7ee2cbSLaszlo Ersek opts_visitor_insert(ov->unprocessed_opts, ov->fake_id_opt); 165eb7ee2cbSLaszlo Ersek } 166*012d4c96SMarkus Armbruster return true; 167eb7ee2cbSLaszlo Ersek } 168eb7ee2cbSLaszlo Ersek 169eb7ee2cbSLaszlo Ersek 170*012d4c96SMarkus Armbruster static bool 17115c2f669SEric Blake opts_check_struct(Visitor *v, Error **errp) 172eb7ee2cbSLaszlo Ersek { 173d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 174f96493b1SEric Blake GHashTableIter iter; 175eb7ee2cbSLaszlo Ersek GQueue *any; 176eb7ee2cbSLaszlo Ersek 17721f88d02SEric Blake if (ov->depth > 1) { 178*012d4c96SMarkus Armbruster return true; 179eb7ee2cbSLaszlo Ersek } 180eb7ee2cbSLaszlo Ersek 181eb7ee2cbSLaszlo Ersek /* we should have processed all (distinct) QemuOpt instances */ 182f96493b1SEric Blake g_hash_table_iter_init(&iter, ov->unprocessed_opts); 183f96493b1SEric Blake if (g_hash_table_iter_next(&iter, NULL, (void **)&any)) { 184eb7ee2cbSLaszlo Ersek const QemuOpt *first; 185eb7ee2cbSLaszlo Ersek 186eb7ee2cbSLaszlo Ersek first = g_queue_peek_head(any); 187c6bd8c70SMarkus Armbruster error_setg(errp, QERR_INVALID_PARAMETER, first->name); 188*012d4c96SMarkus Armbruster return false; 189eb7ee2cbSLaszlo Ersek } 190*012d4c96SMarkus Armbruster return true; 19115c2f669SEric Blake } 19215c2f669SEric Blake 19315c2f669SEric Blake 19415c2f669SEric Blake static void 1951158bb2aSEric Blake opts_end_struct(Visitor *v, void **obj) 19615c2f669SEric Blake { 19715c2f669SEric Blake OptsVisitor *ov = to_ov(v); 19815c2f669SEric Blake 19915c2f669SEric Blake if (--ov->depth > 0) { 20015c2f669SEric Blake return; 20115c2f669SEric Blake } 20215c2f669SEric Blake 203eb7ee2cbSLaszlo Ersek g_hash_table_destroy(ov->unprocessed_opts); 204eb7ee2cbSLaszlo Ersek ov->unprocessed_opts = NULL; 205dc8622f2SChunyan Liu if (ov->fake_id_opt) { 206dc8622f2SChunyan Liu g_free(ov->fake_id_opt->name); 207dc8622f2SChunyan Liu g_free(ov->fake_id_opt->str); 208eb7ee2cbSLaszlo Ersek g_free(ov->fake_id_opt); 209dc8622f2SChunyan Liu } 210eb7ee2cbSLaszlo Ersek ov->fake_id_opt = NULL; 211eb7ee2cbSLaszlo Ersek } 212eb7ee2cbSLaszlo Ersek 213eb7ee2cbSLaszlo Ersek 214eb7ee2cbSLaszlo Ersek static GQueue * 215eb7ee2cbSLaszlo Ersek lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp) 216eb7ee2cbSLaszlo Ersek { 217eb7ee2cbSLaszlo Ersek GQueue *list; 218eb7ee2cbSLaszlo Ersek 219eb7ee2cbSLaszlo Ersek list = g_hash_table_lookup(ov->unprocessed_opts, name); 220eb7ee2cbSLaszlo Ersek if (!list) { 221c6bd8c70SMarkus Armbruster error_setg(errp, QERR_MISSING_PARAMETER, name); 222eb7ee2cbSLaszlo Ersek } 223eb7ee2cbSLaszlo Ersek return list; 224eb7ee2cbSLaszlo Ersek } 225eb7ee2cbSLaszlo Ersek 226eb7ee2cbSLaszlo Ersek 227*012d4c96SMarkus Armbruster static bool 228d9f62ddeSEric Blake opts_start_list(Visitor *v, const char *name, GenericList **list, size_t size, 229d9f62ddeSEric Blake Error **errp) 230eb7ee2cbSLaszlo Ersek { 231d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 232eb7ee2cbSLaszlo Ersek 233eb7ee2cbSLaszlo Ersek /* we can't traverse a list in a list */ 234d9570434SLaszlo Ersek assert(ov->list_mode == LM_NONE); 235d9f62ddeSEric Blake /* we don't support visits without a list */ 236d9f62ddeSEric Blake assert(list); 237eb7ee2cbSLaszlo Ersek ov->repeated_opts = lookup_distinct(ov, name, errp); 238*012d4c96SMarkus Armbruster if (!ov->repeated_opts) { 239*012d4c96SMarkus Armbruster *list = NULL; 240*012d4c96SMarkus Armbruster return false; 241*012d4c96SMarkus Armbruster } 242d9f62ddeSEric Blake ov->list_mode = LM_IN_PROGRESS; 243d9f62ddeSEric Blake *list = g_malloc0(size); 244*012d4c96SMarkus Armbruster return true; 245eb7ee2cbSLaszlo Ersek } 246eb7ee2cbSLaszlo Ersek 247eb7ee2cbSLaszlo Ersek 248eb7ee2cbSLaszlo Ersek static GenericList * 249d9f62ddeSEric Blake opts_next_list(Visitor *v, GenericList *tail, size_t size) 250eb7ee2cbSLaszlo Ersek { 251d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 252eb7ee2cbSLaszlo Ersek 253d9570434SLaszlo Ersek switch (ov->list_mode) { 254863f195fSAndrey Shinkevich case LM_TRAVERSED: 255863f195fSAndrey Shinkevich return NULL; 256d8754f40SLaszlo Ersek case LM_SIGNED_INTERVAL: 257d8754f40SLaszlo Ersek case LM_UNSIGNED_INTERVAL: 258d8754f40SLaszlo Ersek if (ov->list_mode == LM_SIGNED_INTERVAL) { 259d8754f40SLaszlo Ersek if (ov->range_next.s < ov->range_limit.s) { 260d8754f40SLaszlo Ersek ++ov->range_next.s; 261d8754f40SLaszlo Ersek break; 262d8754f40SLaszlo Ersek } 263d8754f40SLaszlo Ersek } else if (ov->range_next.u < ov->range_limit.u) { 264d8754f40SLaszlo Ersek ++ov->range_next.u; 265d8754f40SLaszlo Ersek break; 266d8754f40SLaszlo Ersek } 267d8754f40SLaszlo Ersek ov->list_mode = LM_IN_PROGRESS; 268d8754f40SLaszlo Ersek /* range has been completed, fall through in order to pop option */ 269d8754f40SLaszlo Ersek 270d9570434SLaszlo Ersek case LM_IN_PROGRESS: { 271eb7ee2cbSLaszlo Ersek const QemuOpt *opt; 272eb7ee2cbSLaszlo Ersek 273eb7ee2cbSLaszlo Ersek opt = g_queue_pop_head(ov->repeated_opts); 274eb7ee2cbSLaszlo Ersek if (g_queue_is_empty(ov->repeated_opts)) { 275eb7ee2cbSLaszlo Ersek g_hash_table_remove(ov->unprocessed_opts, opt->name); 276863f195fSAndrey Shinkevich ov->repeated_opts = NULL; 277863f195fSAndrey Shinkevich ov->list_mode = LM_TRAVERSED; 278eb7ee2cbSLaszlo Ersek return NULL; 279eb7ee2cbSLaszlo Ersek } 280d9570434SLaszlo Ersek break; 281d9570434SLaszlo Ersek } 282d9570434SLaszlo Ersek 283d9570434SLaszlo Ersek default: 284d9570434SLaszlo Ersek abort(); 285eb7ee2cbSLaszlo Ersek } 286eb7ee2cbSLaszlo Ersek 287d9f62ddeSEric Blake tail->next = g_malloc0(size); 288d9f62ddeSEric Blake return tail->next; 289eb7ee2cbSLaszlo Ersek } 290eb7ee2cbSLaszlo Ersek 291eb7ee2cbSLaszlo Ersek 292*012d4c96SMarkus Armbruster static bool 293a4a1c70dSMarkus Armbruster opts_check_list(Visitor *v, Error **errp) 294a4a1c70dSMarkus Armbruster { 295a4a1c70dSMarkus Armbruster /* 29621f88d02SEric Blake * Unvisited list elements will be reported later when checking 29721f88d02SEric Blake * whether unvisited struct members remain. 298a4a1c70dSMarkus Armbruster */ 299*012d4c96SMarkus Armbruster return true; 300a4a1c70dSMarkus Armbruster } 301a4a1c70dSMarkus Armbruster 302a4a1c70dSMarkus Armbruster 303a4a1c70dSMarkus Armbruster static void 3041158bb2aSEric Blake opts_end_list(Visitor *v, void **obj) 305eb7ee2cbSLaszlo Ersek { 306d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 307eb7ee2cbSLaszlo Ersek 308d9f62ddeSEric Blake assert(ov->list_mode == LM_IN_PROGRESS || 309d8754f40SLaszlo Ersek ov->list_mode == LM_SIGNED_INTERVAL || 310863f195fSAndrey Shinkevich ov->list_mode == LM_UNSIGNED_INTERVAL || 311863f195fSAndrey Shinkevich ov->list_mode == LM_TRAVERSED); 312eb7ee2cbSLaszlo Ersek ov->repeated_opts = NULL; 313d9570434SLaszlo Ersek ov->list_mode = LM_NONE; 314eb7ee2cbSLaszlo Ersek } 315eb7ee2cbSLaszlo Ersek 316eb7ee2cbSLaszlo Ersek 317eb7ee2cbSLaszlo Ersek static const QemuOpt * 318eb7ee2cbSLaszlo Ersek lookup_scalar(const OptsVisitor *ov, const char *name, Error **errp) 319eb7ee2cbSLaszlo Ersek { 320d9570434SLaszlo Ersek if (ov->list_mode == LM_NONE) { 321eb7ee2cbSLaszlo Ersek GQueue *list; 322eb7ee2cbSLaszlo Ersek 323eb7ee2cbSLaszlo Ersek /* the last occurrence of any QemuOpt takes effect when queried by name 324eb7ee2cbSLaszlo Ersek */ 325eb7ee2cbSLaszlo Ersek list = lookup_distinct(ov, name, errp); 326eb7ee2cbSLaszlo Ersek return list ? g_queue_peek_tail(list) : NULL; 327eb7ee2cbSLaszlo Ersek } 328863f195fSAndrey Shinkevich if (ov->list_mode == LM_TRAVERSED) { 329863f195fSAndrey Shinkevich error_setg(errp, "Fewer list elements than expected"); 330863f195fSAndrey Shinkevich return NULL; 331863f195fSAndrey Shinkevich } 332d9570434SLaszlo Ersek assert(ov->list_mode == LM_IN_PROGRESS); 333eb7ee2cbSLaszlo Ersek return g_queue_peek_head(ov->repeated_opts); 334eb7ee2cbSLaszlo Ersek } 335eb7ee2cbSLaszlo Ersek 336eb7ee2cbSLaszlo Ersek 337eb7ee2cbSLaszlo Ersek static void 338eb7ee2cbSLaszlo Ersek processed(OptsVisitor *ov, const char *name) 339eb7ee2cbSLaszlo Ersek { 340d9570434SLaszlo Ersek if (ov->list_mode == LM_NONE) { 341eb7ee2cbSLaszlo Ersek g_hash_table_remove(ov->unprocessed_opts, name); 342d9570434SLaszlo Ersek return; 343eb7ee2cbSLaszlo Ersek } 344d9570434SLaszlo Ersek assert(ov->list_mode == LM_IN_PROGRESS); 345d9570434SLaszlo Ersek /* do nothing */ 346eb7ee2cbSLaszlo Ersek } 347eb7ee2cbSLaszlo Ersek 348eb7ee2cbSLaszlo Ersek 349*012d4c96SMarkus Armbruster static bool 3500b2a0d6bSEric Blake opts_type_str(Visitor *v, const char *name, char **obj, Error **errp) 351eb7ee2cbSLaszlo Ersek { 352d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 353eb7ee2cbSLaszlo Ersek const QemuOpt *opt; 354eb7ee2cbSLaszlo Ersek 355eb7ee2cbSLaszlo Ersek opt = lookup_scalar(ov, name, errp); 356eb7ee2cbSLaszlo Ersek if (!opt) { 357e58d695eSEric Blake *obj = NULL; 358*012d4c96SMarkus Armbruster return false; 359eb7ee2cbSLaszlo Ersek } 360eb7ee2cbSLaszlo Ersek *obj = g_strdup(opt->str ? opt->str : ""); 361983f52d4SEric Blake /* Note that we consume a string even if this is called as part of 362983f52d4SEric Blake * an enum visit that later fails because the string is not a 363983f52d4SEric Blake * valid enum value; this is harmless because tracking what gets 364983f52d4SEric Blake * consumed only matters to visit_end_struct() as the final error 365983f52d4SEric Blake * check if there were no other failures during the visit. */ 366eb7ee2cbSLaszlo Ersek processed(ov, name); 367*012d4c96SMarkus Armbruster return true; 368eb7ee2cbSLaszlo Ersek } 369eb7ee2cbSLaszlo Ersek 370eb7ee2cbSLaszlo Ersek 371eb7ee2cbSLaszlo Ersek /* mimics qemu-option.c::parse_option_bool() */ 372*012d4c96SMarkus Armbruster static bool 3730b2a0d6bSEric Blake opts_type_bool(Visitor *v, const char *name, bool *obj, Error **errp) 374eb7ee2cbSLaszlo Ersek { 375d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 376eb7ee2cbSLaszlo Ersek const QemuOpt *opt; 377eb7ee2cbSLaszlo Ersek 378eb7ee2cbSLaszlo Ersek opt = lookup_scalar(ov, name, errp); 379eb7ee2cbSLaszlo Ersek if (!opt) { 380*012d4c96SMarkus Armbruster return false; 381eb7ee2cbSLaszlo Ersek } 382eb7ee2cbSLaszlo Ersek 383eb7ee2cbSLaszlo Ersek if (opt->str) { 384eb7ee2cbSLaszlo Ersek if (strcmp(opt->str, "on") == 0 || 385eb7ee2cbSLaszlo Ersek strcmp(opt->str, "yes") == 0 || 386eb7ee2cbSLaszlo Ersek strcmp(opt->str, "y") == 0) { 387eb7ee2cbSLaszlo Ersek *obj = true; 388eb7ee2cbSLaszlo Ersek } else if (strcmp(opt->str, "off") == 0 || 389eb7ee2cbSLaszlo Ersek strcmp(opt->str, "no") == 0 || 390eb7ee2cbSLaszlo Ersek strcmp(opt->str, "n") == 0) { 391eb7ee2cbSLaszlo Ersek *obj = false; 392eb7ee2cbSLaszlo Ersek } else { 393c6bd8c70SMarkus Armbruster error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, 394eb7ee2cbSLaszlo Ersek "on|yes|y|off|no|n"); 395*012d4c96SMarkus Armbruster return false; 396eb7ee2cbSLaszlo Ersek } 397eb7ee2cbSLaszlo Ersek } else { 398eb7ee2cbSLaszlo Ersek *obj = true; 399eb7ee2cbSLaszlo Ersek } 400eb7ee2cbSLaszlo Ersek 401eb7ee2cbSLaszlo Ersek processed(ov, name); 402*012d4c96SMarkus Armbruster return true; 403eb7ee2cbSLaszlo Ersek } 404eb7ee2cbSLaszlo Ersek 405eb7ee2cbSLaszlo Ersek 406*012d4c96SMarkus Armbruster static bool 4070b2a0d6bSEric Blake opts_type_int64(Visitor *v, const char *name, int64_t *obj, Error **errp) 408eb7ee2cbSLaszlo Ersek { 409d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 410eb7ee2cbSLaszlo Ersek const QemuOpt *opt; 411eb7ee2cbSLaszlo Ersek const char *str; 412eb7ee2cbSLaszlo Ersek long long val; 413eb7ee2cbSLaszlo Ersek char *endptr; 414eb7ee2cbSLaszlo Ersek 415d8754f40SLaszlo Ersek if (ov->list_mode == LM_SIGNED_INTERVAL) { 416d8754f40SLaszlo Ersek *obj = ov->range_next.s; 417*012d4c96SMarkus Armbruster return true; 418d8754f40SLaszlo Ersek } 419d8754f40SLaszlo Ersek 420eb7ee2cbSLaszlo Ersek opt = lookup_scalar(ov, name, errp); 421eb7ee2cbSLaszlo Ersek if (!opt) { 422*012d4c96SMarkus Armbruster return false; 423eb7ee2cbSLaszlo Ersek } 424eb7ee2cbSLaszlo Ersek str = opt->str ? opt->str : ""; 425eb7ee2cbSLaszlo Ersek 4261e1c555aSLaszlo Ersek /* we've gotten past lookup_scalar() */ 4271e1c555aSLaszlo Ersek assert(ov->list_mode == LM_NONE || ov->list_mode == LM_IN_PROGRESS); 4281e1c555aSLaszlo Ersek 429eb7ee2cbSLaszlo Ersek errno = 0; 430eb7ee2cbSLaszlo Ersek val = strtoll(str, &endptr, 0); 4311e1c555aSLaszlo Ersek if (errno == 0 && endptr > str && INT64_MIN <= val && val <= INT64_MAX) { 4321e1c555aSLaszlo Ersek if (*endptr == '\0') { 433eb7ee2cbSLaszlo Ersek *obj = val; 434eb7ee2cbSLaszlo Ersek processed(ov, name); 435*012d4c96SMarkus Armbruster return true; 436eb7ee2cbSLaszlo Ersek } 4371e1c555aSLaszlo Ersek if (*endptr == '-' && ov->list_mode == LM_IN_PROGRESS) { 4381e1c555aSLaszlo Ersek long long val2; 4391e1c555aSLaszlo Ersek 4401e1c555aSLaszlo Ersek str = endptr + 1; 4411e1c555aSLaszlo Ersek val2 = strtoll(str, &endptr, 0); 4421e1c555aSLaszlo Ersek if (errno == 0 && endptr > str && *endptr == '\0' && 44315a849beSLaszlo Ersek INT64_MIN <= val2 && val2 <= INT64_MAX && val <= val2 && 44415a849beSLaszlo Ersek (val > INT64_MAX - OPTS_VISITOR_RANGE_MAX || 44515a849beSLaszlo Ersek val2 < val + OPTS_VISITOR_RANGE_MAX)) { 4461e1c555aSLaszlo Ersek ov->range_next.s = val; 4471e1c555aSLaszlo Ersek ov->range_limit.s = val2; 4481e1c555aSLaszlo Ersek ov->list_mode = LM_SIGNED_INTERVAL; 4491e1c555aSLaszlo Ersek 4501e1c555aSLaszlo Ersek /* as if entering on the top */ 4511e1c555aSLaszlo Ersek *obj = ov->range_next.s; 452*012d4c96SMarkus Armbruster return true; 4531e1c555aSLaszlo Ersek } 4541e1c555aSLaszlo Ersek } 4551e1c555aSLaszlo Ersek } 456c6bd8c70SMarkus Armbruster error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, 4571e1c555aSLaszlo Ersek (ov->list_mode == LM_NONE) ? "an int64 value" : 4581e1c555aSLaszlo Ersek "an int64 value or range"); 459*012d4c96SMarkus Armbruster return false; 460eb7ee2cbSLaszlo Ersek } 461eb7ee2cbSLaszlo Ersek 462eb7ee2cbSLaszlo Ersek 463*012d4c96SMarkus Armbruster static bool 4640b2a0d6bSEric Blake opts_type_uint64(Visitor *v, const char *name, uint64_t *obj, Error **errp) 465eb7ee2cbSLaszlo Ersek { 466d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 467eb7ee2cbSLaszlo Ersek const QemuOpt *opt; 468eb7ee2cbSLaszlo Ersek const char *str; 46962d090e2SLaszlo Ersek unsigned long long val; 470581a8a80SLaszlo Ersek char *endptr; 471eb7ee2cbSLaszlo Ersek 472d8754f40SLaszlo Ersek if (ov->list_mode == LM_UNSIGNED_INTERVAL) { 473d8754f40SLaszlo Ersek *obj = ov->range_next.u; 474*012d4c96SMarkus Armbruster return true; 475d8754f40SLaszlo Ersek } 476d8754f40SLaszlo Ersek 477eb7ee2cbSLaszlo Ersek opt = lookup_scalar(ov, name, errp); 478eb7ee2cbSLaszlo Ersek if (!opt) { 479*012d4c96SMarkus Armbruster return false; 480eb7ee2cbSLaszlo Ersek } 481eb7ee2cbSLaszlo Ersek str = opt->str; 482eb7ee2cbSLaszlo Ersek 483581a8a80SLaszlo Ersek /* we've gotten past lookup_scalar() */ 484581a8a80SLaszlo Ersek assert(ov->list_mode == LM_NONE || ov->list_mode == LM_IN_PROGRESS); 485581a8a80SLaszlo Ersek 486581a8a80SLaszlo Ersek if (parse_uint(str, &val, &endptr, 0) == 0 && val <= UINT64_MAX) { 487581a8a80SLaszlo Ersek if (*endptr == '\0') { 488eb7ee2cbSLaszlo Ersek *obj = val; 489eb7ee2cbSLaszlo Ersek processed(ov, name); 490*012d4c96SMarkus Armbruster return true; 491eb7ee2cbSLaszlo Ersek } 492581a8a80SLaszlo Ersek if (*endptr == '-' && ov->list_mode == LM_IN_PROGRESS) { 493581a8a80SLaszlo Ersek unsigned long long val2; 494581a8a80SLaszlo Ersek 495581a8a80SLaszlo Ersek str = endptr + 1; 496581a8a80SLaszlo Ersek if (parse_uint_full(str, &val2, 0) == 0 && 49715a849beSLaszlo Ersek val2 <= UINT64_MAX && val <= val2 && 49815a849beSLaszlo Ersek val2 - val < OPTS_VISITOR_RANGE_MAX) { 499581a8a80SLaszlo Ersek ov->range_next.u = val; 500581a8a80SLaszlo Ersek ov->range_limit.u = val2; 501581a8a80SLaszlo Ersek ov->list_mode = LM_UNSIGNED_INTERVAL; 502581a8a80SLaszlo Ersek 503581a8a80SLaszlo Ersek /* as if entering on the top */ 504581a8a80SLaszlo Ersek *obj = ov->range_next.u; 505*012d4c96SMarkus Armbruster return true; 506581a8a80SLaszlo Ersek } 507581a8a80SLaszlo Ersek } 508581a8a80SLaszlo Ersek } 509c6bd8c70SMarkus Armbruster error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, 510581a8a80SLaszlo Ersek (ov->list_mode == LM_NONE) ? "a uint64 value" : 511581a8a80SLaszlo Ersek "a uint64 value or range"); 512*012d4c96SMarkus Armbruster return false; 513eb7ee2cbSLaszlo Ersek } 514eb7ee2cbSLaszlo Ersek 515eb7ee2cbSLaszlo Ersek 516*012d4c96SMarkus Armbruster static bool 5170b2a0d6bSEric Blake opts_type_size(Visitor *v, const char *name, uint64_t *obj, Error **errp) 518eb7ee2cbSLaszlo Ersek { 519d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 520eb7ee2cbSLaszlo Ersek const QemuOpt *opt; 521f17fd4fdSMarkus Armbruster int err; 522eb7ee2cbSLaszlo Ersek 523eb7ee2cbSLaszlo Ersek opt = lookup_scalar(ov, name, errp); 524eb7ee2cbSLaszlo Ersek if (!opt) { 525*012d4c96SMarkus Armbruster return false; 526eb7ee2cbSLaszlo Ersek } 527eb7ee2cbSLaszlo Ersek 528f46bfdbfSMarkus Armbruster err = qemu_strtosz(opt->str ? opt->str : "", NULL, obj); 529f17fd4fdSMarkus Armbruster if (err < 0) { 530c6bd8c70SMarkus Armbruster error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, 531f46bfdbfSMarkus Armbruster "a size value"); 532*012d4c96SMarkus Armbruster return false; 533cb45de67SAmos Kong } 534cb45de67SAmos Kong 535cb45de67SAmos Kong processed(ov, name); 536*012d4c96SMarkus Armbruster return true; 537eb7ee2cbSLaszlo Ersek } 538eb7ee2cbSLaszlo Ersek 539eb7ee2cbSLaszlo Ersek 540eb7ee2cbSLaszlo Ersek static void 5410b2a0d6bSEric Blake opts_optional(Visitor *v, const char *name, bool *present) 542eb7ee2cbSLaszlo Ersek { 543d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 544eb7ee2cbSLaszlo Ersek 545eb7ee2cbSLaszlo Ersek /* we only support a single mandatory scalar field in a list node */ 546d9570434SLaszlo Ersek assert(ov->list_mode == LM_NONE); 547eb7ee2cbSLaszlo Ersek *present = (lookup_distinct(ov, name, NULL) != NULL); 548eb7ee2cbSLaszlo Ersek } 549eb7ee2cbSLaszlo Ersek 550eb7ee2cbSLaszlo Ersek 5512c0ef9f4SEric Blake static void 5522c0ef9f4SEric Blake opts_free(Visitor *v) 5532c0ef9f4SEric Blake { 5542c0ef9f4SEric Blake OptsVisitor *ov = to_ov(v); 5552c0ef9f4SEric Blake 55609204eacSEric Blake if (ov->unprocessed_opts != NULL) { 55709204eacSEric Blake g_hash_table_destroy(ov->unprocessed_opts); 55809204eacSEric Blake } 55909204eacSEric Blake g_free(ov->fake_id_opt); 56009204eacSEric Blake g_free(ov); 5612c0ef9f4SEric Blake } 5622c0ef9f4SEric Blake 5632c0ef9f4SEric Blake 56409204eacSEric Blake Visitor * 565eb7ee2cbSLaszlo Ersek opts_visitor_new(const QemuOpts *opts) 566eb7ee2cbSLaszlo Ersek { 567eb7ee2cbSLaszlo Ersek OptsVisitor *ov; 568eb7ee2cbSLaszlo Ersek 569f332e830SMarkus Armbruster assert(opts); 570eb7ee2cbSLaszlo Ersek ov = g_malloc0(sizeof *ov); 571eb7ee2cbSLaszlo Ersek 572983f52d4SEric Blake ov->visitor.type = VISITOR_INPUT; 573983f52d4SEric Blake 574eb7ee2cbSLaszlo Ersek ov->visitor.start_struct = &opts_start_struct; 57515c2f669SEric Blake ov->visitor.check_struct = &opts_check_struct; 576eb7ee2cbSLaszlo Ersek ov->visitor.end_struct = &opts_end_struct; 577eb7ee2cbSLaszlo Ersek 578eb7ee2cbSLaszlo Ersek ov->visitor.start_list = &opts_start_list; 579eb7ee2cbSLaszlo Ersek ov->visitor.next_list = &opts_next_list; 580a4a1c70dSMarkus Armbruster ov->visitor.check_list = &opts_check_list; 581eb7ee2cbSLaszlo Ersek ov->visitor.end_list = &opts_end_list; 582eb7ee2cbSLaszlo Ersek 5834c40314aSEric Blake ov->visitor.type_int64 = &opts_type_int64; 584eb7ee2cbSLaszlo Ersek ov->visitor.type_uint64 = &opts_type_uint64; 585eb7ee2cbSLaszlo Ersek ov->visitor.type_size = &opts_type_size; 586eb7ee2cbSLaszlo Ersek ov->visitor.type_bool = &opts_type_bool; 587eb7ee2cbSLaszlo Ersek ov->visitor.type_str = &opts_type_str; 588eb7ee2cbSLaszlo Ersek 589eb7ee2cbSLaszlo Ersek /* type_number() is not filled in, but this is not the first visitor to 590eb7ee2cbSLaszlo Ersek * skip some mandatory methods... */ 591eb7ee2cbSLaszlo Ersek 592e2cd0f4fSMarkus Armbruster ov->visitor.optional = &opts_optional; 5932c0ef9f4SEric Blake ov->visitor.free = opts_free; 594eb7ee2cbSLaszlo Ersek 595eb7ee2cbSLaszlo Ersek ov->opts_root = opts; 596eb7ee2cbSLaszlo Ersek 597eb7ee2cbSLaszlo Ersek return &ov->visitor; 598eb7ee2cbSLaszlo Ersek } 599