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 27d9f62ddeSEric Blake LM_IN_PROGRESS, /* opts_next_list() ready to be called. 28d8754f40SLaszlo Ersek * 29d8754f40SLaszlo Ersek * Generating the next list link will consume the most 30d8754f40SLaszlo Ersek * recently parsed QemuOpt instance of the repeated 31d8754f40SLaszlo Ersek * option. 32d8754f40SLaszlo Ersek * 33d8754f40SLaszlo Ersek * Parsing a value into the list link will examine the 34d8754f40SLaszlo Ersek * next QemuOpt instance of the repeated option, and 35d8754f40SLaszlo Ersek * possibly enter LM_SIGNED_INTERVAL or 36d8754f40SLaszlo Ersek * LM_UNSIGNED_INTERVAL. 37d8754f40SLaszlo Ersek */ 38d8754f40SLaszlo Ersek 39d8754f40SLaszlo Ersek LM_SIGNED_INTERVAL, /* opts_next_list() has been called. 40d8754f40SLaszlo Ersek * 41d8754f40SLaszlo Ersek * Generating the next list link will consume the most 42d8754f40SLaszlo Ersek * recently stored element from the signed interval, 43d8754f40SLaszlo Ersek * parsed from the most recent QemuOpt instance of the 44d8754f40SLaszlo Ersek * repeated option. This may consume QemuOpt itself 45d8754f40SLaszlo Ersek * and return to LM_IN_PROGRESS. 46d8754f40SLaszlo Ersek * 47d8754f40SLaszlo Ersek * Parsing a value into the list link will store the 48d8754f40SLaszlo Ersek * next element of the signed interval. 49d8754f40SLaszlo Ersek */ 50d8754f40SLaszlo Ersek 51d8754f40SLaszlo Ersek LM_UNSIGNED_INTERVAL /* Same as above, only for an unsigned interval. */ 52d9570434SLaszlo Ersek }; 53d9570434SLaszlo Ersek 54d9570434SLaszlo Ersek typedef enum ListMode ListMode; 55d9570434SLaszlo Ersek 56eb7ee2cbSLaszlo Ersek struct OptsVisitor 57eb7ee2cbSLaszlo Ersek { 58eb7ee2cbSLaszlo Ersek Visitor visitor; 59eb7ee2cbSLaszlo Ersek 60eb7ee2cbSLaszlo Ersek /* Ownership remains with opts_visitor_new()'s caller. */ 61eb7ee2cbSLaszlo Ersek const QemuOpts *opts_root; 62eb7ee2cbSLaszlo Ersek 63eb7ee2cbSLaszlo Ersek unsigned depth; 64eb7ee2cbSLaszlo Ersek 65eb7ee2cbSLaszlo Ersek /* Non-null iff depth is positive. Each key is a QemuOpt name. Each value 66eb7ee2cbSLaszlo Ersek * is a non-empty GQueue, enumerating all QemuOpt occurrences with that 67eb7ee2cbSLaszlo Ersek * name. */ 68eb7ee2cbSLaszlo Ersek GHashTable *unprocessed_opts; 69eb7ee2cbSLaszlo Ersek 70eb7ee2cbSLaszlo Ersek /* The list currently being traversed with opts_start_list() / 71eb7ee2cbSLaszlo Ersek * opts_next_list(). The list must have a struct element type in the 72eb7ee2cbSLaszlo Ersek * schema, with a single mandatory scalar member. */ 73d9570434SLaszlo Ersek ListMode list_mode; 74eb7ee2cbSLaszlo Ersek GQueue *repeated_opts; 75eb7ee2cbSLaszlo Ersek 76d8754f40SLaszlo Ersek /* When parsing a list of repeating options as integers, values of the form 77d8754f40SLaszlo Ersek * "a-b", representing a closed interval, are allowed. Elements in the 78d8754f40SLaszlo Ersek * range are generated individually. 79d8754f40SLaszlo Ersek */ 80d8754f40SLaszlo Ersek union { 81d8754f40SLaszlo Ersek int64_t s; 82d8754f40SLaszlo Ersek uint64_t u; 83d8754f40SLaszlo Ersek } range_next, range_limit; 84d8754f40SLaszlo Ersek 85eb7ee2cbSLaszlo Ersek /* If "opts_root->id" is set, reinstantiate it as a fake QemuOpt for 86eb7ee2cbSLaszlo Ersek * uniformity. Only its "name" and "str" fields are set. "fake_id_opt" does 87eb7ee2cbSLaszlo Ersek * not survive or escape the OptsVisitor object. 88eb7ee2cbSLaszlo Ersek */ 89eb7ee2cbSLaszlo Ersek QemuOpt *fake_id_opt; 90eb7ee2cbSLaszlo Ersek }; 91eb7ee2cbSLaszlo Ersek 92eb7ee2cbSLaszlo Ersek 93d7bea75dSEric Blake static OptsVisitor *to_ov(Visitor *v) 94d7bea75dSEric Blake { 95d7bea75dSEric Blake return container_of(v, OptsVisitor, visitor); 96d7bea75dSEric Blake } 97d7bea75dSEric Blake 98d7bea75dSEric Blake 99eb7ee2cbSLaszlo Ersek static void 100eb7ee2cbSLaszlo Ersek destroy_list(gpointer list) 101eb7ee2cbSLaszlo Ersek { 102eb7ee2cbSLaszlo Ersek g_queue_free(list); 103eb7ee2cbSLaszlo Ersek } 104eb7ee2cbSLaszlo Ersek 105eb7ee2cbSLaszlo Ersek 106eb7ee2cbSLaszlo Ersek static void 107eb7ee2cbSLaszlo Ersek opts_visitor_insert(GHashTable *unprocessed_opts, const QemuOpt *opt) 108eb7ee2cbSLaszlo Ersek { 109eb7ee2cbSLaszlo Ersek GQueue *list; 110eb7ee2cbSLaszlo Ersek 111eb7ee2cbSLaszlo Ersek list = g_hash_table_lookup(unprocessed_opts, opt->name); 112eb7ee2cbSLaszlo Ersek if (list == NULL) { 113eb7ee2cbSLaszlo Ersek list = g_queue_new(); 114eb7ee2cbSLaszlo Ersek 115eb7ee2cbSLaszlo Ersek /* GHashTable will never try to free the keys -- we supply NULL as 116eb7ee2cbSLaszlo Ersek * "key_destroy_func" in opts_start_struct(). Thus cast away key 117eb7ee2cbSLaszlo Ersek * const-ness in order to suppress gcc's warning. 118eb7ee2cbSLaszlo Ersek */ 119eb7ee2cbSLaszlo Ersek g_hash_table_insert(unprocessed_opts, (gpointer)opt->name, list); 120eb7ee2cbSLaszlo Ersek } 121eb7ee2cbSLaszlo Ersek 122eb7ee2cbSLaszlo Ersek /* Similarly, destroy_list() doesn't call g_queue_free_full(). */ 123eb7ee2cbSLaszlo Ersek g_queue_push_tail(list, (gpointer)opt); 124eb7ee2cbSLaszlo Ersek } 125eb7ee2cbSLaszlo Ersek 126eb7ee2cbSLaszlo Ersek 127eb7ee2cbSLaszlo Ersek static void 128337283dfSEric Blake opts_start_struct(Visitor *v, const char *name, void **obj, 1290b2a0d6bSEric Blake size_t size, Error **errp) 130eb7ee2cbSLaszlo Ersek { 131d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 132eb7ee2cbSLaszlo Ersek const QemuOpt *opt; 133eb7ee2cbSLaszlo Ersek 134b7745397SMarkus Armbruster if (obj) { 135e58d695eSEric Blake *obj = g_malloc0(size); 136b7745397SMarkus Armbruster } 137eb7ee2cbSLaszlo Ersek if (ov->depth++ > 0) { 138eb7ee2cbSLaszlo Ersek return; 139eb7ee2cbSLaszlo Ersek } 140eb7ee2cbSLaszlo Ersek 141eb7ee2cbSLaszlo Ersek ov->unprocessed_opts = g_hash_table_new_full(&g_str_hash, &g_str_equal, 142eb7ee2cbSLaszlo Ersek NULL, &destroy_list); 143eb7ee2cbSLaszlo Ersek QTAILQ_FOREACH(opt, &ov->opts_root->head, next) { 144eb7ee2cbSLaszlo Ersek /* ensured by qemu-option.c::opts_do_parse() */ 145eb7ee2cbSLaszlo Ersek assert(strcmp(opt->name, "id") != 0); 146eb7ee2cbSLaszlo Ersek 147eb7ee2cbSLaszlo Ersek opts_visitor_insert(ov->unprocessed_opts, opt); 148eb7ee2cbSLaszlo Ersek } 149eb7ee2cbSLaszlo Ersek 150eb7ee2cbSLaszlo Ersek if (ov->opts_root->id != NULL) { 151eb7ee2cbSLaszlo Ersek ov->fake_id_opt = g_malloc0(sizeof *ov->fake_id_opt); 152eb7ee2cbSLaszlo Ersek 153dc8622f2SChunyan Liu ov->fake_id_opt->name = g_strdup("id"); 154dc8622f2SChunyan Liu ov->fake_id_opt->str = g_strdup(ov->opts_root->id); 155eb7ee2cbSLaszlo Ersek opts_visitor_insert(ov->unprocessed_opts, ov->fake_id_opt); 156eb7ee2cbSLaszlo Ersek } 157eb7ee2cbSLaszlo Ersek } 158eb7ee2cbSLaszlo Ersek 159eb7ee2cbSLaszlo Ersek 160eb7ee2cbSLaszlo Ersek static void 16115c2f669SEric Blake opts_check_struct(Visitor *v, Error **errp) 162eb7ee2cbSLaszlo Ersek { 163d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 164f96493b1SEric Blake GHashTableIter iter; 165eb7ee2cbSLaszlo Ersek GQueue *any; 166eb7ee2cbSLaszlo Ersek 167*21f88d02SEric Blake if (ov->depth > 1) { 168eb7ee2cbSLaszlo Ersek return; 169eb7ee2cbSLaszlo Ersek } 170eb7ee2cbSLaszlo Ersek 171eb7ee2cbSLaszlo Ersek /* we should have processed all (distinct) QemuOpt instances */ 172f96493b1SEric Blake g_hash_table_iter_init(&iter, ov->unprocessed_opts); 173f96493b1SEric Blake if (g_hash_table_iter_next(&iter, NULL, (void **)&any)) { 174eb7ee2cbSLaszlo Ersek const QemuOpt *first; 175eb7ee2cbSLaszlo Ersek 176eb7ee2cbSLaszlo Ersek first = g_queue_peek_head(any); 177c6bd8c70SMarkus Armbruster error_setg(errp, QERR_INVALID_PARAMETER, first->name); 178eb7ee2cbSLaszlo Ersek } 17915c2f669SEric Blake } 18015c2f669SEric Blake 18115c2f669SEric Blake 18215c2f669SEric Blake static void 1831158bb2aSEric Blake opts_end_struct(Visitor *v, void **obj) 18415c2f669SEric Blake { 18515c2f669SEric Blake OptsVisitor *ov = to_ov(v); 18615c2f669SEric Blake 18715c2f669SEric Blake if (--ov->depth > 0) { 18815c2f669SEric Blake return; 18915c2f669SEric Blake } 19015c2f669SEric Blake 191eb7ee2cbSLaszlo Ersek g_hash_table_destroy(ov->unprocessed_opts); 192eb7ee2cbSLaszlo Ersek ov->unprocessed_opts = NULL; 193dc8622f2SChunyan Liu if (ov->fake_id_opt) { 194dc8622f2SChunyan Liu g_free(ov->fake_id_opt->name); 195dc8622f2SChunyan Liu g_free(ov->fake_id_opt->str); 196eb7ee2cbSLaszlo Ersek g_free(ov->fake_id_opt); 197dc8622f2SChunyan Liu } 198eb7ee2cbSLaszlo Ersek ov->fake_id_opt = NULL; 199eb7ee2cbSLaszlo Ersek } 200eb7ee2cbSLaszlo Ersek 201eb7ee2cbSLaszlo Ersek 202eb7ee2cbSLaszlo Ersek static GQueue * 203eb7ee2cbSLaszlo Ersek lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp) 204eb7ee2cbSLaszlo Ersek { 205eb7ee2cbSLaszlo Ersek GQueue *list; 206eb7ee2cbSLaszlo Ersek 207eb7ee2cbSLaszlo Ersek list = g_hash_table_lookup(ov->unprocessed_opts, name); 208eb7ee2cbSLaszlo Ersek if (!list) { 209c6bd8c70SMarkus Armbruster error_setg(errp, QERR_MISSING_PARAMETER, name); 210eb7ee2cbSLaszlo Ersek } 211eb7ee2cbSLaszlo Ersek return list; 212eb7ee2cbSLaszlo Ersek } 213eb7ee2cbSLaszlo Ersek 214eb7ee2cbSLaszlo Ersek 215eb7ee2cbSLaszlo Ersek static void 216d9f62ddeSEric Blake opts_start_list(Visitor *v, const char *name, GenericList **list, size_t size, 217d9f62ddeSEric Blake Error **errp) 218eb7ee2cbSLaszlo Ersek { 219d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 220eb7ee2cbSLaszlo Ersek 221eb7ee2cbSLaszlo Ersek /* we can't traverse a list in a list */ 222d9570434SLaszlo Ersek assert(ov->list_mode == LM_NONE); 223d9f62ddeSEric Blake /* we don't support visits without a list */ 224d9f62ddeSEric Blake assert(list); 225eb7ee2cbSLaszlo Ersek ov->repeated_opts = lookup_distinct(ov, name, errp); 226d9f62ddeSEric Blake if (ov->repeated_opts) { 227d9f62ddeSEric Blake ov->list_mode = LM_IN_PROGRESS; 228d9f62ddeSEric Blake *list = g_malloc0(size); 229d9f62ddeSEric Blake } else { 230d9f62ddeSEric Blake *list = NULL; 231d9570434SLaszlo Ersek } 232eb7ee2cbSLaszlo Ersek } 233eb7ee2cbSLaszlo Ersek 234eb7ee2cbSLaszlo Ersek 235eb7ee2cbSLaszlo Ersek static GenericList * 236d9f62ddeSEric Blake opts_next_list(Visitor *v, GenericList *tail, size_t size) 237eb7ee2cbSLaszlo Ersek { 238d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 239eb7ee2cbSLaszlo Ersek 240d9570434SLaszlo Ersek switch (ov->list_mode) { 241d8754f40SLaszlo Ersek case LM_SIGNED_INTERVAL: 242d8754f40SLaszlo Ersek case LM_UNSIGNED_INTERVAL: 243d8754f40SLaszlo Ersek if (ov->list_mode == LM_SIGNED_INTERVAL) { 244d8754f40SLaszlo Ersek if (ov->range_next.s < ov->range_limit.s) { 245d8754f40SLaszlo Ersek ++ov->range_next.s; 246d8754f40SLaszlo Ersek break; 247d8754f40SLaszlo Ersek } 248d8754f40SLaszlo Ersek } else if (ov->range_next.u < ov->range_limit.u) { 249d8754f40SLaszlo Ersek ++ov->range_next.u; 250d8754f40SLaszlo Ersek break; 251d8754f40SLaszlo Ersek } 252d8754f40SLaszlo Ersek ov->list_mode = LM_IN_PROGRESS; 253d8754f40SLaszlo Ersek /* range has been completed, fall through in order to pop option */ 254d8754f40SLaszlo Ersek 255d9570434SLaszlo Ersek case LM_IN_PROGRESS: { 256eb7ee2cbSLaszlo Ersek const QemuOpt *opt; 257eb7ee2cbSLaszlo Ersek 258eb7ee2cbSLaszlo Ersek opt = g_queue_pop_head(ov->repeated_opts); 259eb7ee2cbSLaszlo Ersek if (g_queue_is_empty(ov->repeated_opts)) { 260eb7ee2cbSLaszlo Ersek g_hash_table_remove(ov->unprocessed_opts, opt->name); 261eb7ee2cbSLaszlo Ersek return NULL; 262eb7ee2cbSLaszlo Ersek } 263d9570434SLaszlo Ersek break; 264d9570434SLaszlo Ersek } 265d9570434SLaszlo Ersek 266d9570434SLaszlo Ersek default: 267d9570434SLaszlo Ersek abort(); 268eb7ee2cbSLaszlo Ersek } 269eb7ee2cbSLaszlo Ersek 270d9f62ddeSEric Blake tail->next = g_malloc0(size); 271d9f62ddeSEric Blake return tail->next; 272eb7ee2cbSLaszlo Ersek } 273eb7ee2cbSLaszlo Ersek 274eb7ee2cbSLaszlo Ersek 275eb7ee2cbSLaszlo Ersek static void 276a4a1c70dSMarkus Armbruster opts_check_list(Visitor *v, Error **errp) 277a4a1c70dSMarkus Armbruster { 278a4a1c70dSMarkus Armbruster /* 279*21f88d02SEric Blake * Unvisited list elements will be reported later when checking 280*21f88d02SEric Blake * whether unvisited struct members remain. 281a4a1c70dSMarkus Armbruster */ 282a4a1c70dSMarkus Armbruster } 283a4a1c70dSMarkus Armbruster 284a4a1c70dSMarkus Armbruster 285a4a1c70dSMarkus Armbruster static void 2861158bb2aSEric Blake opts_end_list(Visitor *v, void **obj) 287eb7ee2cbSLaszlo Ersek { 288d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 289eb7ee2cbSLaszlo Ersek 290d9f62ddeSEric Blake assert(ov->list_mode == LM_IN_PROGRESS || 291d8754f40SLaszlo Ersek ov->list_mode == LM_SIGNED_INTERVAL || 292d8754f40SLaszlo Ersek ov->list_mode == LM_UNSIGNED_INTERVAL); 293eb7ee2cbSLaszlo Ersek ov->repeated_opts = NULL; 294d9570434SLaszlo Ersek ov->list_mode = LM_NONE; 295eb7ee2cbSLaszlo Ersek } 296eb7ee2cbSLaszlo Ersek 297eb7ee2cbSLaszlo Ersek 298eb7ee2cbSLaszlo Ersek static const QemuOpt * 299eb7ee2cbSLaszlo Ersek lookup_scalar(const OptsVisitor *ov, const char *name, Error **errp) 300eb7ee2cbSLaszlo Ersek { 301d9570434SLaszlo Ersek if (ov->list_mode == LM_NONE) { 302eb7ee2cbSLaszlo Ersek GQueue *list; 303eb7ee2cbSLaszlo Ersek 304eb7ee2cbSLaszlo Ersek /* the last occurrence of any QemuOpt takes effect when queried by name 305eb7ee2cbSLaszlo Ersek */ 306eb7ee2cbSLaszlo Ersek list = lookup_distinct(ov, name, errp); 307eb7ee2cbSLaszlo Ersek return list ? g_queue_peek_tail(list) : NULL; 308eb7ee2cbSLaszlo Ersek } 309d9570434SLaszlo Ersek assert(ov->list_mode == LM_IN_PROGRESS); 310eb7ee2cbSLaszlo Ersek return g_queue_peek_head(ov->repeated_opts); 311eb7ee2cbSLaszlo Ersek } 312eb7ee2cbSLaszlo Ersek 313eb7ee2cbSLaszlo Ersek 314eb7ee2cbSLaszlo Ersek static void 315eb7ee2cbSLaszlo Ersek processed(OptsVisitor *ov, const char *name) 316eb7ee2cbSLaszlo Ersek { 317d9570434SLaszlo Ersek if (ov->list_mode == LM_NONE) { 318eb7ee2cbSLaszlo Ersek g_hash_table_remove(ov->unprocessed_opts, name); 319d9570434SLaszlo Ersek return; 320eb7ee2cbSLaszlo Ersek } 321d9570434SLaszlo Ersek assert(ov->list_mode == LM_IN_PROGRESS); 322d9570434SLaszlo Ersek /* do nothing */ 323eb7ee2cbSLaszlo Ersek } 324eb7ee2cbSLaszlo Ersek 325eb7ee2cbSLaszlo Ersek 326eb7ee2cbSLaszlo Ersek static void 3270b2a0d6bSEric Blake opts_type_str(Visitor *v, const char *name, char **obj, Error **errp) 328eb7ee2cbSLaszlo Ersek { 329d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 330eb7ee2cbSLaszlo Ersek const QemuOpt *opt; 331eb7ee2cbSLaszlo Ersek 332eb7ee2cbSLaszlo Ersek opt = lookup_scalar(ov, name, errp); 333eb7ee2cbSLaszlo Ersek if (!opt) { 334e58d695eSEric Blake *obj = NULL; 335eb7ee2cbSLaszlo Ersek return; 336eb7ee2cbSLaszlo Ersek } 337eb7ee2cbSLaszlo Ersek *obj = g_strdup(opt->str ? opt->str : ""); 338983f52d4SEric Blake /* Note that we consume a string even if this is called as part of 339983f52d4SEric Blake * an enum visit that later fails because the string is not a 340983f52d4SEric Blake * valid enum value; this is harmless because tracking what gets 341983f52d4SEric Blake * consumed only matters to visit_end_struct() as the final error 342983f52d4SEric Blake * check if there were no other failures during the visit. */ 343eb7ee2cbSLaszlo Ersek processed(ov, name); 344eb7ee2cbSLaszlo Ersek } 345eb7ee2cbSLaszlo Ersek 346eb7ee2cbSLaszlo Ersek 347eb7ee2cbSLaszlo Ersek /* mimics qemu-option.c::parse_option_bool() */ 348eb7ee2cbSLaszlo Ersek static void 3490b2a0d6bSEric Blake opts_type_bool(Visitor *v, const char *name, bool *obj, Error **errp) 350eb7ee2cbSLaszlo Ersek { 351d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 352eb7ee2cbSLaszlo Ersek const QemuOpt *opt; 353eb7ee2cbSLaszlo Ersek 354eb7ee2cbSLaszlo Ersek opt = lookup_scalar(ov, name, errp); 355eb7ee2cbSLaszlo Ersek if (!opt) { 356eb7ee2cbSLaszlo Ersek return; 357eb7ee2cbSLaszlo Ersek } 358eb7ee2cbSLaszlo Ersek 359eb7ee2cbSLaszlo Ersek if (opt->str) { 360eb7ee2cbSLaszlo Ersek if (strcmp(opt->str, "on") == 0 || 361eb7ee2cbSLaszlo Ersek strcmp(opt->str, "yes") == 0 || 362eb7ee2cbSLaszlo Ersek strcmp(opt->str, "y") == 0) { 363eb7ee2cbSLaszlo Ersek *obj = true; 364eb7ee2cbSLaszlo Ersek } else if (strcmp(opt->str, "off") == 0 || 365eb7ee2cbSLaszlo Ersek strcmp(opt->str, "no") == 0 || 366eb7ee2cbSLaszlo Ersek strcmp(opt->str, "n") == 0) { 367eb7ee2cbSLaszlo Ersek *obj = false; 368eb7ee2cbSLaszlo Ersek } else { 369c6bd8c70SMarkus Armbruster error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, 370eb7ee2cbSLaszlo Ersek "on|yes|y|off|no|n"); 371eb7ee2cbSLaszlo Ersek return; 372eb7ee2cbSLaszlo Ersek } 373eb7ee2cbSLaszlo Ersek } else { 374eb7ee2cbSLaszlo Ersek *obj = true; 375eb7ee2cbSLaszlo Ersek } 376eb7ee2cbSLaszlo Ersek 377eb7ee2cbSLaszlo Ersek processed(ov, name); 378eb7ee2cbSLaszlo Ersek } 379eb7ee2cbSLaszlo Ersek 380eb7ee2cbSLaszlo Ersek 381eb7ee2cbSLaszlo Ersek static void 3820b2a0d6bSEric Blake opts_type_int64(Visitor *v, const char *name, int64_t *obj, Error **errp) 383eb7ee2cbSLaszlo Ersek { 384d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 385eb7ee2cbSLaszlo Ersek const QemuOpt *opt; 386eb7ee2cbSLaszlo Ersek const char *str; 387eb7ee2cbSLaszlo Ersek long long val; 388eb7ee2cbSLaszlo Ersek char *endptr; 389eb7ee2cbSLaszlo Ersek 390d8754f40SLaszlo Ersek if (ov->list_mode == LM_SIGNED_INTERVAL) { 391d8754f40SLaszlo Ersek *obj = ov->range_next.s; 392d8754f40SLaszlo Ersek return; 393d8754f40SLaszlo Ersek } 394d8754f40SLaszlo Ersek 395eb7ee2cbSLaszlo Ersek opt = lookup_scalar(ov, name, errp); 396eb7ee2cbSLaszlo Ersek if (!opt) { 397eb7ee2cbSLaszlo Ersek return; 398eb7ee2cbSLaszlo Ersek } 399eb7ee2cbSLaszlo Ersek str = opt->str ? opt->str : ""; 400eb7ee2cbSLaszlo Ersek 4011e1c555aSLaszlo Ersek /* we've gotten past lookup_scalar() */ 4021e1c555aSLaszlo Ersek assert(ov->list_mode == LM_NONE || ov->list_mode == LM_IN_PROGRESS); 4031e1c555aSLaszlo Ersek 404eb7ee2cbSLaszlo Ersek errno = 0; 405eb7ee2cbSLaszlo Ersek val = strtoll(str, &endptr, 0); 4061e1c555aSLaszlo Ersek if (errno == 0 && endptr > str && INT64_MIN <= val && val <= INT64_MAX) { 4071e1c555aSLaszlo Ersek if (*endptr == '\0') { 408eb7ee2cbSLaszlo Ersek *obj = val; 409eb7ee2cbSLaszlo Ersek processed(ov, name); 410eb7ee2cbSLaszlo Ersek return; 411eb7ee2cbSLaszlo Ersek } 4121e1c555aSLaszlo Ersek if (*endptr == '-' && ov->list_mode == LM_IN_PROGRESS) { 4131e1c555aSLaszlo Ersek long long val2; 4141e1c555aSLaszlo Ersek 4151e1c555aSLaszlo Ersek str = endptr + 1; 4161e1c555aSLaszlo Ersek val2 = strtoll(str, &endptr, 0); 4171e1c555aSLaszlo Ersek if (errno == 0 && endptr > str && *endptr == '\0' && 41815a849beSLaszlo Ersek INT64_MIN <= val2 && val2 <= INT64_MAX && val <= val2 && 41915a849beSLaszlo Ersek (val > INT64_MAX - OPTS_VISITOR_RANGE_MAX || 42015a849beSLaszlo Ersek val2 < val + OPTS_VISITOR_RANGE_MAX)) { 4211e1c555aSLaszlo Ersek ov->range_next.s = val; 4221e1c555aSLaszlo Ersek ov->range_limit.s = val2; 4231e1c555aSLaszlo Ersek ov->list_mode = LM_SIGNED_INTERVAL; 4241e1c555aSLaszlo Ersek 4251e1c555aSLaszlo Ersek /* as if entering on the top */ 4261e1c555aSLaszlo Ersek *obj = ov->range_next.s; 4271e1c555aSLaszlo Ersek return; 4281e1c555aSLaszlo Ersek } 4291e1c555aSLaszlo Ersek } 4301e1c555aSLaszlo Ersek } 431c6bd8c70SMarkus Armbruster error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, 4321e1c555aSLaszlo Ersek (ov->list_mode == LM_NONE) ? "an int64 value" : 4331e1c555aSLaszlo Ersek "an int64 value or range"); 434eb7ee2cbSLaszlo Ersek } 435eb7ee2cbSLaszlo Ersek 436eb7ee2cbSLaszlo Ersek 437eb7ee2cbSLaszlo Ersek static void 4380b2a0d6bSEric Blake opts_type_uint64(Visitor *v, const char *name, uint64_t *obj, Error **errp) 439eb7ee2cbSLaszlo Ersek { 440d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 441eb7ee2cbSLaszlo Ersek const QemuOpt *opt; 442eb7ee2cbSLaszlo Ersek const char *str; 44362d090e2SLaszlo Ersek unsigned long long val; 444581a8a80SLaszlo Ersek char *endptr; 445eb7ee2cbSLaszlo Ersek 446d8754f40SLaszlo Ersek if (ov->list_mode == LM_UNSIGNED_INTERVAL) { 447d8754f40SLaszlo Ersek *obj = ov->range_next.u; 448d8754f40SLaszlo Ersek return; 449d8754f40SLaszlo Ersek } 450d8754f40SLaszlo Ersek 451eb7ee2cbSLaszlo Ersek opt = lookup_scalar(ov, name, errp); 452eb7ee2cbSLaszlo Ersek if (!opt) { 453eb7ee2cbSLaszlo Ersek return; 454eb7ee2cbSLaszlo Ersek } 455eb7ee2cbSLaszlo Ersek str = opt->str; 456eb7ee2cbSLaszlo Ersek 457581a8a80SLaszlo Ersek /* we've gotten past lookup_scalar() */ 458581a8a80SLaszlo Ersek assert(ov->list_mode == LM_NONE || ov->list_mode == LM_IN_PROGRESS); 459581a8a80SLaszlo Ersek 460581a8a80SLaszlo Ersek if (parse_uint(str, &val, &endptr, 0) == 0 && val <= UINT64_MAX) { 461581a8a80SLaszlo Ersek if (*endptr == '\0') { 462eb7ee2cbSLaszlo Ersek *obj = val; 463eb7ee2cbSLaszlo Ersek processed(ov, name); 464eb7ee2cbSLaszlo Ersek return; 465eb7ee2cbSLaszlo Ersek } 466581a8a80SLaszlo Ersek if (*endptr == '-' && ov->list_mode == LM_IN_PROGRESS) { 467581a8a80SLaszlo Ersek unsigned long long val2; 468581a8a80SLaszlo Ersek 469581a8a80SLaszlo Ersek str = endptr + 1; 470581a8a80SLaszlo Ersek if (parse_uint_full(str, &val2, 0) == 0 && 47115a849beSLaszlo Ersek val2 <= UINT64_MAX && val <= val2 && 47215a849beSLaszlo Ersek val2 - val < OPTS_VISITOR_RANGE_MAX) { 473581a8a80SLaszlo Ersek ov->range_next.u = val; 474581a8a80SLaszlo Ersek ov->range_limit.u = val2; 475581a8a80SLaszlo Ersek ov->list_mode = LM_UNSIGNED_INTERVAL; 476581a8a80SLaszlo Ersek 477581a8a80SLaszlo Ersek /* as if entering on the top */ 478581a8a80SLaszlo Ersek *obj = ov->range_next.u; 479581a8a80SLaszlo Ersek return; 480581a8a80SLaszlo Ersek } 481581a8a80SLaszlo Ersek } 482581a8a80SLaszlo Ersek } 483c6bd8c70SMarkus Armbruster error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, 484581a8a80SLaszlo Ersek (ov->list_mode == LM_NONE) ? "a uint64 value" : 485581a8a80SLaszlo Ersek "a uint64 value or range"); 486eb7ee2cbSLaszlo Ersek } 487eb7ee2cbSLaszlo Ersek 488eb7ee2cbSLaszlo Ersek 489eb7ee2cbSLaszlo Ersek static void 4900b2a0d6bSEric Blake opts_type_size(Visitor *v, const char *name, uint64_t *obj, Error **errp) 491eb7ee2cbSLaszlo Ersek { 492d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 493eb7ee2cbSLaszlo Ersek const QemuOpt *opt; 494f17fd4fdSMarkus Armbruster int err; 495eb7ee2cbSLaszlo Ersek 496eb7ee2cbSLaszlo Ersek opt = lookup_scalar(ov, name, errp); 497eb7ee2cbSLaszlo Ersek if (!opt) { 498eb7ee2cbSLaszlo Ersek return; 499eb7ee2cbSLaszlo Ersek } 500eb7ee2cbSLaszlo Ersek 501f46bfdbfSMarkus Armbruster err = qemu_strtosz(opt->str ? opt->str : "", NULL, obj); 502f17fd4fdSMarkus Armbruster if (err < 0) { 503c6bd8c70SMarkus Armbruster error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, 504f46bfdbfSMarkus Armbruster "a size value"); 505cb45de67SAmos Kong return; 506cb45de67SAmos Kong } 507cb45de67SAmos Kong 508cb45de67SAmos Kong processed(ov, name); 509eb7ee2cbSLaszlo Ersek } 510eb7ee2cbSLaszlo Ersek 511eb7ee2cbSLaszlo Ersek 512eb7ee2cbSLaszlo Ersek static void 5130b2a0d6bSEric Blake opts_optional(Visitor *v, const char *name, bool *present) 514eb7ee2cbSLaszlo Ersek { 515d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 516eb7ee2cbSLaszlo Ersek 517eb7ee2cbSLaszlo Ersek /* we only support a single mandatory scalar field in a list node */ 518d9570434SLaszlo Ersek assert(ov->list_mode == LM_NONE); 519eb7ee2cbSLaszlo Ersek *present = (lookup_distinct(ov, name, NULL) != NULL); 520eb7ee2cbSLaszlo Ersek } 521eb7ee2cbSLaszlo Ersek 522eb7ee2cbSLaszlo Ersek 5232c0ef9f4SEric Blake static void 5242c0ef9f4SEric Blake opts_free(Visitor *v) 5252c0ef9f4SEric Blake { 5262c0ef9f4SEric Blake OptsVisitor *ov = to_ov(v); 5272c0ef9f4SEric Blake 52809204eacSEric Blake if (ov->unprocessed_opts != NULL) { 52909204eacSEric Blake g_hash_table_destroy(ov->unprocessed_opts); 53009204eacSEric Blake } 53109204eacSEric Blake g_free(ov->fake_id_opt); 53209204eacSEric Blake g_free(ov); 5332c0ef9f4SEric Blake } 5342c0ef9f4SEric Blake 5352c0ef9f4SEric Blake 53609204eacSEric Blake Visitor * 537eb7ee2cbSLaszlo Ersek opts_visitor_new(const QemuOpts *opts) 538eb7ee2cbSLaszlo Ersek { 539eb7ee2cbSLaszlo Ersek OptsVisitor *ov; 540eb7ee2cbSLaszlo Ersek 541f332e830SMarkus Armbruster assert(opts); 542eb7ee2cbSLaszlo Ersek ov = g_malloc0(sizeof *ov); 543eb7ee2cbSLaszlo Ersek 544983f52d4SEric Blake ov->visitor.type = VISITOR_INPUT; 545983f52d4SEric Blake 546eb7ee2cbSLaszlo Ersek ov->visitor.start_struct = &opts_start_struct; 54715c2f669SEric Blake ov->visitor.check_struct = &opts_check_struct; 548eb7ee2cbSLaszlo Ersek ov->visitor.end_struct = &opts_end_struct; 549eb7ee2cbSLaszlo Ersek 550eb7ee2cbSLaszlo Ersek ov->visitor.start_list = &opts_start_list; 551eb7ee2cbSLaszlo Ersek ov->visitor.next_list = &opts_next_list; 552a4a1c70dSMarkus Armbruster ov->visitor.check_list = &opts_check_list; 553eb7ee2cbSLaszlo Ersek ov->visitor.end_list = &opts_end_list; 554eb7ee2cbSLaszlo Ersek 5554c40314aSEric Blake ov->visitor.type_int64 = &opts_type_int64; 556eb7ee2cbSLaszlo Ersek ov->visitor.type_uint64 = &opts_type_uint64; 557eb7ee2cbSLaszlo Ersek ov->visitor.type_size = &opts_type_size; 558eb7ee2cbSLaszlo Ersek ov->visitor.type_bool = &opts_type_bool; 559eb7ee2cbSLaszlo Ersek ov->visitor.type_str = &opts_type_str; 560eb7ee2cbSLaszlo Ersek 561eb7ee2cbSLaszlo Ersek /* type_number() is not filled in, but this is not the first visitor to 562eb7ee2cbSLaszlo Ersek * skip some mandatory methods... */ 563eb7ee2cbSLaszlo Ersek 564e2cd0f4fSMarkus Armbruster ov->visitor.optional = &opts_optional; 5652c0ef9f4SEric Blake ov->visitor.free = opts_free; 566eb7ee2cbSLaszlo Ersek 567eb7ee2cbSLaszlo Ersek ov->opts_root = opts; 568eb7ee2cbSLaszlo Ersek 569eb7ee2cbSLaszlo Ersek return &ov->visitor; 570eb7ee2cbSLaszlo Ersek } 571