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 */ 26d9570434SLaszlo Ersek LM_STARTED, /* opts_start_list() succeeded */ 27d8754f40SLaszlo Ersek 28d8754f40SLaszlo Ersek LM_IN_PROGRESS, /* opts_next_list() has been 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 40d8754f40SLaszlo Ersek LM_SIGNED_INTERVAL, /* opts_next_list() has been called. 41d8754f40SLaszlo Ersek * 42d8754f40SLaszlo Ersek * Generating the next list link will consume the most 43d8754f40SLaszlo Ersek * recently stored element from the signed interval, 44d8754f40SLaszlo Ersek * parsed from the most recent QemuOpt instance of the 45d8754f40SLaszlo Ersek * repeated option. This may consume QemuOpt itself 46d8754f40SLaszlo Ersek * and return to LM_IN_PROGRESS. 47d8754f40SLaszlo Ersek * 48d8754f40SLaszlo Ersek * Parsing a value into the list link will store the 49d8754f40SLaszlo Ersek * next element of the signed interval. 50d8754f40SLaszlo Ersek */ 51d8754f40SLaszlo Ersek 52d8754f40SLaszlo Ersek LM_UNSIGNED_INTERVAL /* Same as above, only for an unsigned interval. */ 53d9570434SLaszlo Ersek }; 54d9570434SLaszlo Ersek 55d9570434SLaszlo Ersek typedef enum ListMode ListMode; 56d9570434SLaszlo Ersek 57eb7ee2cbSLaszlo Ersek struct OptsVisitor 58eb7ee2cbSLaszlo Ersek { 59eb7ee2cbSLaszlo Ersek Visitor visitor; 60eb7ee2cbSLaszlo Ersek 61eb7ee2cbSLaszlo Ersek /* Ownership remains with opts_visitor_new()'s caller. */ 62eb7ee2cbSLaszlo Ersek const QemuOpts *opts_root; 63eb7ee2cbSLaszlo Ersek 64eb7ee2cbSLaszlo Ersek unsigned depth; 65eb7ee2cbSLaszlo Ersek 66eb7ee2cbSLaszlo Ersek /* Non-null iff depth is positive. Each key is a QemuOpt name. Each value 67eb7ee2cbSLaszlo Ersek * is a non-empty GQueue, enumerating all QemuOpt occurrences with that 68eb7ee2cbSLaszlo Ersek * name. */ 69eb7ee2cbSLaszlo Ersek GHashTable *unprocessed_opts; 70eb7ee2cbSLaszlo Ersek 71eb7ee2cbSLaszlo Ersek /* The list currently being traversed with opts_start_list() / 72eb7ee2cbSLaszlo Ersek * opts_next_list(). The list must have a struct element type in the 73eb7ee2cbSLaszlo Ersek * schema, with a single mandatory scalar member. */ 74d9570434SLaszlo Ersek ListMode list_mode; 75eb7ee2cbSLaszlo Ersek GQueue *repeated_opts; 76eb7ee2cbSLaszlo Ersek 77d8754f40SLaszlo Ersek /* When parsing a list of repeating options as integers, values of the form 78d8754f40SLaszlo Ersek * "a-b", representing a closed interval, are allowed. Elements in the 79d8754f40SLaszlo Ersek * range are generated individually. 80d8754f40SLaszlo Ersek */ 81d8754f40SLaszlo Ersek union { 82d8754f40SLaszlo Ersek int64_t s; 83d8754f40SLaszlo Ersek uint64_t u; 84d8754f40SLaszlo Ersek } range_next, range_limit; 85d8754f40SLaszlo Ersek 86eb7ee2cbSLaszlo Ersek /* If "opts_root->id" is set, reinstantiate it as a fake QemuOpt for 87eb7ee2cbSLaszlo Ersek * uniformity. Only its "name" and "str" fields are set. "fake_id_opt" does 88eb7ee2cbSLaszlo Ersek * not survive or escape the OptsVisitor object. 89eb7ee2cbSLaszlo Ersek */ 90eb7ee2cbSLaszlo Ersek QemuOpt *fake_id_opt; 91eb7ee2cbSLaszlo Ersek }; 92eb7ee2cbSLaszlo Ersek 93eb7ee2cbSLaszlo Ersek 94d7bea75dSEric Blake static OptsVisitor *to_ov(Visitor *v) 95d7bea75dSEric Blake { 96d7bea75dSEric Blake return container_of(v, OptsVisitor, visitor); 97d7bea75dSEric Blake } 98d7bea75dSEric Blake 99d7bea75dSEric Blake 100eb7ee2cbSLaszlo Ersek static void 101eb7ee2cbSLaszlo Ersek destroy_list(gpointer list) 102eb7ee2cbSLaszlo Ersek { 103eb7ee2cbSLaszlo Ersek g_queue_free(list); 104eb7ee2cbSLaszlo Ersek } 105eb7ee2cbSLaszlo Ersek 106eb7ee2cbSLaszlo Ersek 107eb7ee2cbSLaszlo Ersek static void 108eb7ee2cbSLaszlo Ersek opts_visitor_insert(GHashTable *unprocessed_opts, const QemuOpt *opt) 109eb7ee2cbSLaszlo Ersek { 110eb7ee2cbSLaszlo Ersek GQueue *list; 111eb7ee2cbSLaszlo Ersek 112eb7ee2cbSLaszlo Ersek list = g_hash_table_lookup(unprocessed_opts, opt->name); 113eb7ee2cbSLaszlo Ersek if (list == NULL) { 114eb7ee2cbSLaszlo Ersek list = g_queue_new(); 115eb7ee2cbSLaszlo Ersek 116eb7ee2cbSLaszlo Ersek /* GHashTable will never try to free the keys -- we supply NULL as 117eb7ee2cbSLaszlo Ersek * "key_destroy_func" in opts_start_struct(). Thus cast away key 118eb7ee2cbSLaszlo Ersek * const-ness in order to suppress gcc's warning. 119eb7ee2cbSLaszlo Ersek */ 120eb7ee2cbSLaszlo Ersek g_hash_table_insert(unprocessed_opts, (gpointer)opt->name, list); 121eb7ee2cbSLaszlo Ersek } 122eb7ee2cbSLaszlo Ersek 123eb7ee2cbSLaszlo Ersek /* Similarly, destroy_list() doesn't call g_queue_free_full(). */ 124eb7ee2cbSLaszlo Ersek g_queue_push_tail(list, (gpointer)opt); 125eb7ee2cbSLaszlo Ersek } 126eb7ee2cbSLaszlo Ersek 127eb7ee2cbSLaszlo Ersek 128eb7ee2cbSLaszlo Ersek static void 129337283dfSEric Blake opts_start_struct(Visitor *v, const char *name, void **obj, 1300b2a0d6bSEric Blake size_t size, Error **errp) 131eb7ee2cbSLaszlo Ersek { 132d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 133eb7ee2cbSLaszlo Ersek const QemuOpt *opt; 134eb7ee2cbSLaszlo Ersek 135b7745397SMarkus Armbruster if (obj) { 136eb7ee2cbSLaszlo Ersek *obj = g_malloc0(size > 0 ? size : 1); 137b7745397SMarkus Armbruster } 138eb7ee2cbSLaszlo Ersek if (ov->depth++ > 0) { 139eb7ee2cbSLaszlo Ersek return; 140eb7ee2cbSLaszlo Ersek } 141eb7ee2cbSLaszlo Ersek 142eb7ee2cbSLaszlo Ersek ov->unprocessed_opts = g_hash_table_new_full(&g_str_hash, &g_str_equal, 143eb7ee2cbSLaszlo Ersek NULL, &destroy_list); 144eb7ee2cbSLaszlo Ersek QTAILQ_FOREACH(opt, &ov->opts_root->head, next) { 145eb7ee2cbSLaszlo Ersek /* ensured by qemu-option.c::opts_do_parse() */ 146eb7ee2cbSLaszlo Ersek assert(strcmp(opt->name, "id") != 0); 147eb7ee2cbSLaszlo Ersek 148eb7ee2cbSLaszlo Ersek opts_visitor_insert(ov->unprocessed_opts, opt); 149eb7ee2cbSLaszlo Ersek } 150eb7ee2cbSLaszlo Ersek 151eb7ee2cbSLaszlo Ersek if (ov->opts_root->id != NULL) { 152eb7ee2cbSLaszlo Ersek ov->fake_id_opt = g_malloc0(sizeof *ov->fake_id_opt); 153eb7ee2cbSLaszlo Ersek 154dc8622f2SChunyan Liu ov->fake_id_opt->name = g_strdup("id"); 155dc8622f2SChunyan Liu ov->fake_id_opt->str = g_strdup(ov->opts_root->id); 156eb7ee2cbSLaszlo Ersek opts_visitor_insert(ov->unprocessed_opts, ov->fake_id_opt); 157eb7ee2cbSLaszlo Ersek } 158eb7ee2cbSLaszlo Ersek } 159eb7ee2cbSLaszlo Ersek 160eb7ee2cbSLaszlo Ersek 161eb7ee2cbSLaszlo Ersek static void 162eb7ee2cbSLaszlo Ersek opts_end_struct(Visitor *v, Error **errp) 163eb7ee2cbSLaszlo Ersek { 164d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 165f96493b1SEric Blake GHashTableIter iter; 166eb7ee2cbSLaszlo Ersek GQueue *any; 167eb7ee2cbSLaszlo Ersek 168eb7ee2cbSLaszlo Ersek if (--ov->depth > 0) { 169eb7ee2cbSLaszlo Ersek return; 170eb7ee2cbSLaszlo Ersek } 171eb7ee2cbSLaszlo Ersek 172eb7ee2cbSLaszlo Ersek /* we should have processed all (distinct) QemuOpt instances */ 173f96493b1SEric Blake g_hash_table_iter_init(&iter, ov->unprocessed_opts); 174f96493b1SEric Blake if (g_hash_table_iter_next(&iter, NULL, (void **)&any)) { 175eb7ee2cbSLaszlo Ersek const QemuOpt *first; 176eb7ee2cbSLaszlo Ersek 177eb7ee2cbSLaszlo Ersek first = g_queue_peek_head(any); 178c6bd8c70SMarkus Armbruster error_setg(errp, QERR_INVALID_PARAMETER, first->name); 179eb7ee2cbSLaszlo Ersek } 180eb7ee2cbSLaszlo Ersek g_hash_table_destroy(ov->unprocessed_opts); 181eb7ee2cbSLaszlo Ersek ov->unprocessed_opts = NULL; 182dc8622f2SChunyan Liu if (ov->fake_id_opt) { 183dc8622f2SChunyan Liu g_free(ov->fake_id_opt->name); 184dc8622f2SChunyan Liu g_free(ov->fake_id_opt->str); 185eb7ee2cbSLaszlo Ersek g_free(ov->fake_id_opt); 186dc8622f2SChunyan Liu } 187eb7ee2cbSLaszlo Ersek ov->fake_id_opt = NULL; 188eb7ee2cbSLaszlo Ersek } 189eb7ee2cbSLaszlo Ersek 190eb7ee2cbSLaszlo Ersek 191eb7ee2cbSLaszlo Ersek static GQueue * 192eb7ee2cbSLaszlo Ersek lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp) 193eb7ee2cbSLaszlo Ersek { 194eb7ee2cbSLaszlo Ersek GQueue *list; 195eb7ee2cbSLaszlo Ersek 196eb7ee2cbSLaszlo Ersek list = g_hash_table_lookup(ov->unprocessed_opts, name); 197eb7ee2cbSLaszlo Ersek if (!list) { 198c6bd8c70SMarkus Armbruster error_setg(errp, QERR_MISSING_PARAMETER, name); 199eb7ee2cbSLaszlo Ersek } 200eb7ee2cbSLaszlo Ersek return list; 201eb7ee2cbSLaszlo Ersek } 202eb7ee2cbSLaszlo Ersek 203eb7ee2cbSLaszlo Ersek 204eb7ee2cbSLaszlo Ersek static void 205eb7ee2cbSLaszlo Ersek opts_start_list(Visitor *v, const char *name, Error **errp) 206eb7ee2cbSLaszlo Ersek { 207d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 208eb7ee2cbSLaszlo Ersek 209eb7ee2cbSLaszlo Ersek /* we can't traverse a list in a list */ 210d9570434SLaszlo Ersek assert(ov->list_mode == LM_NONE); 211eb7ee2cbSLaszlo Ersek ov->repeated_opts = lookup_distinct(ov, name, errp); 212d9570434SLaszlo Ersek if (ov->repeated_opts != NULL) { 213d9570434SLaszlo Ersek ov->list_mode = LM_STARTED; 214d9570434SLaszlo Ersek } 215eb7ee2cbSLaszlo Ersek } 216eb7ee2cbSLaszlo Ersek 217eb7ee2cbSLaszlo Ersek 218eb7ee2cbSLaszlo Ersek static GenericList * 219e65d89bfSEric Blake opts_next_list(Visitor *v, GenericList **list, size_t size) 220eb7ee2cbSLaszlo Ersek { 221d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 222eb7ee2cbSLaszlo Ersek GenericList **link; 223eb7ee2cbSLaszlo Ersek 224d9570434SLaszlo Ersek switch (ov->list_mode) { 225d9570434SLaszlo Ersek case LM_STARTED: 226d9570434SLaszlo Ersek ov->list_mode = LM_IN_PROGRESS; 227eb7ee2cbSLaszlo Ersek link = list; 228d9570434SLaszlo Ersek break; 229d9570434SLaszlo Ersek 230d8754f40SLaszlo Ersek case LM_SIGNED_INTERVAL: 231d8754f40SLaszlo Ersek case LM_UNSIGNED_INTERVAL: 232d8754f40SLaszlo Ersek link = &(*list)->next; 233d8754f40SLaszlo Ersek 234d8754f40SLaszlo Ersek if (ov->list_mode == LM_SIGNED_INTERVAL) { 235d8754f40SLaszlo Ersek if (ov->range_next.s < ov->range_limit.s) { 236d8754f40SLaszlo Ersek ++ov->range_next.s; 237d8754f40SLaszlo Ersek break; 238d8754f40SLaszlo Ersek } 239d8754f40SLaszlo Ersek } else if (ov->range_next.u < ov->range_limit.u) { 240d8754f40SLaszlo Ersek ++ov->range_next.u; 241d8754f40SLaszlo Ersek break; 242d8754f40SLaszlo Ersek } 243d8754f40SLaszlo Ersek ov->list_mode = LM_IN_PROGRESS; 244d8754f40SLaszlo Ersek /* range has been completed, fall through in order to pop option */ 245d8754f40SLaszlo Ersek 246d9570434SLaszlo Ersek case LM_IN_PROGRESS: { 247eb7ee2cbSLaszlo Ersek const QemuOpt *opt; 248eb7ee2cbSLaszlo Ersek 249eb7ee2cbSLaszlo Ersek opt = g_queue_pop_head(ov->repeated_opts); 250eb7ee2cbSLaszlo Ersek if (g_queue_is_empty(ov->repeated_opts)) { 251eb7ee2cbSLaszlo Ersek g_hash_table_remove(ov->unprocessed_opts, opt->name); 252eb7ee2cbSLaszlo Ersek return NULL; 253eb7ee2cbSLaszlo Ersek } 254eb7ee2cbSLaszlo Ersek link = &(*list)->next; 255d9570434SLaszlo Ersek break; 256d9570434SLaszlo Ersek } 257d9570434SLaszlo Ersek 258d9570434SLaszlo Ersek default: 259d9570434SLaszlo Ersek abort(); 260eb7ee2cbSLaszlo Ersek } 261eb7ee2cbSLaszlo Ersek 262e65d89bfSEric Blake *link = g_malloc0(size); 263eb7ee2cbSLaszlo Ersek return *link; 264eb7ee2cbSLaszlo Ersek } 265eb7ee2cbSLaszlo Ersek 266eb7ee2cbSLaszlo Ersek 267eb7ee2cbSLaszlo Ersek static void 26808f9541dSEric Blake opts_end_list(Visitor *v) 269eb7ee2cbSLaszlo Ersek { 270d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 271eb7ee2cbSLaszlo Ersek 272d8754f40SLaszlo Ersek assert(ov->list_mode == LM_STARTED || 273d8754f40SLaszlo Ersek ov->list_mode == LM_IN_PROGRESS || 274d8754f40SLaszlo Ersek ov->list_mode == LM_SIGNED_INTERVAL || 275d8754f40SLaszlo Ersek ov->list_mode == LM_UNSIGNED_INTERVAL); 276eb7ee2cbSLaszlo Ersek ov->repeated_opts = NULL; 277d9570434SLaszlo Ersek ov->list_mode = LM_NONE; 278eb7ee2cbSLaszlo Ersek } 279eb7ee2cbSLaszlo Ersek 280eb7ee2cbSLaszlo Ersek 281eb7ee2cbSLaszlo Ersek static const QemuOpt * 282eb7ee2cbSLaszlo Ersek lookup_scalar(const OptsVisitor *ov, const char *name, Error **errp) 283eb7ee2cbSLaszlo Ersek { 284d9570434SLaszlo Ersek if (ov->list_mode == LM_NONE) { 285eb7ee2cbSLaszlo Ersek GQueue *list; 286eb7ee2cbSLaszlo Ersek 287eb7ee2cbSLaszlo Ersek /* the last occurrence of any QemuOpt takes effect when queried by name 288eb7ee2cbSLaszlo Ersek */ 289eb7ee2cbSLaszlo Ersek list = lookup_distinct(ov, name, errp); 290eb7ee2cbSLaszlo Ersek return list ? g_queue_peek_tail(list) : NULL; 291eb7ee2cbSLaszlo Ersek } 292d9570434SLaszlo Ersek assert(ov->list_mode == LM_IN_PROGRESS); 293eb7ee2cbSLaszlo Ersek return g_queue_peek_head(ov->repeated_opts); 294eb7ee2cbSLaszlo Ersek } 295eb7ee2cbSLaszlo Ersek 296eb7ee2cbSLaszlo Ersek 297eb7ee2cbSLaszlo Ersek static void 298eb7ee2cbSLaszlo Ersek processed(OptsVisitor *ov, const char *name) 299eb7ee2cbSLaszlo Ersek { 300d9570434SLaszlo Ersek if (ov->list_mode == LM_NONE) { 301eb7ee2cbSLaszlo Ersek g_hash_table_remove(ov->unprocessed_opts, name); 302d9570434SLaszlo Ersek return; 303eb7ee2cbSLaszlo Ersek } 304d9570434SLaszlo Ersek assert(ov->list_mode == LM_IN_PROGRESS); 305d9570434SLaszlo Ersek /* do nothing */ 306eb7ee2cbSLaszlo Ersek } 307eb7ee2cbSLaszlo Ersek 308eb7ee2cbSLaszlo Ersek 309eb7ee2cbSLaszlo Ersek static void 3100b2a0d6bSEric Blake opts_type_str(Visitor *v, const char *name, char **obj, Error **errp) 311eb7ee2cbSLaszlo Ersek { 312d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 313eb7ee2cbSLaszlo Ersek const QemuOpt *opt; 314eb7ee2cbSLaszlo Ersek 315eb7ee2cbSLaszlo Ersek opt = lookup_scalar(ov, name, errp); 316eb7ee2cbSLaszlo Ersek if (!opt) { 317eb7ee2cbSLaszlo Ersek return; 318eb7ee2cbSLaszlo Ersek } 319eb7ee2cbSLaszlo Ersek *obj = g_strdup(opt->str ? opt->str : ""); 320*983f52d4SEric Blake /* Note that we consume a string even if this is called as part of 321*983f52d4SEric Blake * an enum visit that later fails because the string is not a 322*983f52d4SEric Blake * valid enum value; this is harmless because tracking what gets 323*983f52d4SEric Blake * consumed only matters to visit_end_struct() as the final error 324*983f52d4SEric Blake * check if there were no other failures during the visit. */ 325eb7ee2cbSLaszlo Ersek processed(ov, name); 326eb7ee2cbSLaszlo Ersek } 327eb7ee2cbSLaszlo Ersek 328eb7ee2cbSLaszlo Ersek 329eb7ee2cbSLaszlo Ersek /* mimics qemu-option.c::parse_option_bool() */ 330eb7ee2cbSLaszlo Ersek static void 3310b2a0d6bSEric Blake opts_type_bool(Visitor *v, const char *name, bool *obj, Error **errp) 332eb7ee2cbSLaszlo Ersek { 333d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 334eb7ee2cbSLaszlo Ersek const QemuOpt *opt; 335eb7ee2cbSLaszlo Ersek 336eb7ee2cbSLaszlo Ersek opt = lookup_scalar(ov, name, errp); 337eb7ee2cbSLaszlo Ersek if (!opt) { 338eb7ee2cbSLaszlo Ersek return; 339eb7ee2cbSLaszlo Ersek } 340eb7ee2cbSLaszlo Ersek 341eb7ee2cbSLaszlo Ersek if (opt->str) { 342eb7ee2cbSLaszlo Ersek if (strcmp(opt->str, "on") == 0 || 343eb7ee2cbSLaszlo Ersek strcmp(opt->str, "yes") == 0 || 344eb7ee2cbSLaszlo Ersek strcmp(opt->str, "y") == 0) { 345eb7ee2cbSLaszlo Ersek *obj = true; 346eb7ee2cbSLaszlo Ersek } else if (strcmp(opt->str, "off") == 0 || 347eb7ee2cbSLaszlo Ersek strcmp(opt->str, "no") == 0 || 348eb7ee2cbSLaszlo Ersek strcmp(opt->str, "n") == 0) { 349eb7ee2cbSLaszlo Ersek *obj = false; 350eb7ee2cbSLaszlo Ersek } else { 351c6bd8c70SMarkus Armbruster error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, 352eb7ee2cbSLaszlo Ersek "on|yes|y|off|no|n"); 353eb7ee2cbSLaszlo Ersek return; 354eb7ee2cbSLaszlo Ersek } 355eb7ee2cbSLaszlo Ersek } else { 356eb7ee2cbSLaszlo Ersek *obj = true; 357eb7ee2cbSLaszlo Ersek } 358eb7ee2cbSLaszlo Ersek 359eb7ee2cbSLaszlo Ersek processed(ov, name); 360eb7ee2cbSLaszlo Ersek } 361eb7ee2cbSLaszlo Ersek 362eb7ee2cbSLaszlo Ersek 363eb7ee2cbSLaszlo Ersek static void 3640b2a0d6bSEric Blake opts_type_int64(Visitor *v, const char *name, int64_t *obj, Error **errp) 365eb7ee2cbSLaszlo Ersek { 366d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 367eb7ee2cbSLaszlo Ersek const QemuOpt *opt; 368eb7ee2cbSLaszlo Ersek const char *str; 369eb7ee2cbSLaszlo Ersek long long val; 370eb7ee2cbSLaszlo Ersek char *endptr; 371eb7ee2cbSLaszlo Ersek 372d8754f40SLaszlo Ersek if (ov->list_mode == LM_SIGNED_INTERVAL) { 373d8754f40SLaszlo Ersek *obj = ov->range_next.s; 374d8754f40SLaszlo Ersek return; 375d8754f40SLaszlo Ersek } 376d8754f40SLaszlo Ersek 377eb7ee2cbSLaszlo Ersek opt = lookup_scalar(ov, name, errp); 378eb7ee2cbSLaszlo Ersek if (!opt) { 379eb7ee2cbSLaszlo Ersek return; 380eb7ee2cbSLaszlo Ersek } 381eb7ee2cbSLaszlo Ersek str = opt->str ? opt->str : ""; 382eb7ee2cbSLaszlo Ersek 3831e1c555aSLaszlo Ersek /* we've gotten past lookup_scalar() */ 3841e1c555aSLaszlo Ersek assert(ov->list_mode == LM_NONE || ov->list_mode == LM_IN_PROGRESS); 3851e1c555aSLaszlo Ersek 386eb7ee2cbSLaszlo Ersek errno = 0; 387eb7ee2cbSLaszlo Ersek val = strtoll(str, &endptr, 0); 3881e1c555aSLaszlo Ersek if (errno == 0 && endptr > str && INT64_MIN <= val && val <= INT64_MAX) { 3891e1c555aSLaszlo Ersek if (*endptr == '\0') { 390eb7ee2cbSLaszlo Ersek *obj = val; 391eb7ee2cbSLaszlo Ersek processed(ov, name); 392eb7ee2cbSLaszlo Ersek return; 393eb7ee2cbSLaszlo Ersek } 3941e1c555aSLaszlo Ersek if (*endptr == '-' && ov->list_mode == LM_IN_PROGRESS) { 3951e1c555aSLaszlo Ersek long long val2; 3961e1c555aSLaszlo Ersek 3971e1c555aSLaszlo Ersek str = endptr + 1; 3981e1c555aSLaszlo Ersek val2 = strtoll(str, &endptr, 0); 3991e1c555aSLaszlo Ersek if (errno == 0 && endptr > str && *endptr == '\0' && 40015a849beSLaszlo Ersek INT64_MIN <= val2 && val2 <= INT64_MAX && val <= val2 && 40115a849beSLaszlo Ersek (val > INT64_MAX - OPTS_VISITOR_RANGE_MAX || 40215a849beSLaszlo Ersek val2 < val + OPTS_VISITOR_RANGE_MAX)) { 4031e1c555aSLaszlo Ersek ov->range_next.s = val; 4041e1c555aSLaszlo Ersek ov->range_limit.s = val2; 4051e1c555aSLaszlo Ersek ov->list_mode = LM_SIGNED_INTERVAL; 4061e1c555aSLaszlo Ersek 4071e1c555aSLaszlo Ersek /* as if entering on the top */ 4081e1c555aSLaszlo Ersek *obj = ov->range_next.s; 4091e1c555aSLaszlo Ersek return; 4101e1c555aSLaszlo Ersek } 4111e1c555aSLaszlo Ersek } 4121e1c555aSLaszlo Ersek } 413c6bd8c70SMarkus Armbruster error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, 4141e1c555aSLaszlo Ersek (ov->list_mode == LM_NONE) ? "an int64 value" : 4151e1c555aSLaszlo Ersek "an int64 value or range"); 416eb7ee2cbSLaszlo Ersek } 417eb7ee2cbSLaszlo Ersek 418eb7ee2cbSLaszlo Ersek 419eb7ee2cbSLaszlo Ersek static void 4200b2a0d6bSEric Blake opts_type_uint64(Visitor *v, const char *name, uint64_t *obj, Error **errp) 421eb7ee2cbSLaszlo Ersek { 422d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 423eb7ee2cbSLaszlo Ersek const QemuOpt *opt; 424eb7ee2cbSLaszlo Ersek const char *str; 42562d090e2SLaszlo Ersek unsigned long long val; 426581a8a80SLaszlo Ersek char *endptr; 427eb7ee2cbSLaszlo Ersek 428d8754f40SLaszlo Ersek if (ov->list_mode == LM_UNSIGNED_INTERVAL) { 429d8754f40SLaszlo Ersek *obj = ov->range_next.u; 430d8754f40SLaszlo Ersek return; 431d8754f40SLaszlo Ersek } 432d8754f40SLaszlo Ersek 433eb7ee2cbSLaszlo Ersek opt = lookup_scalar(ov, name, errp); 434eb7ee2cbSLaszlo Ersek if (!opt) { 435eb7ee2cbSLaszlo Ersek return; 436eb7ee2cbSLaszlo Ersek } 437eb7ee2cbSLaszlo Ersek str = opt->str; 438eb7ee2cbSLaszlo Ersek 439581a8a80SLaszlo Ersek /* we've gotten past lookup_scalar() */ 440581a8a80SLaszlo Ersek assert(ov->list_mode == LM_NONE || ov->list_mode == LM_IN_PROGRESS); 441581a8a80SLaszlo Ersek 442581a8a80SLaszlo Ersek if (parse_uint(str, &val, &endptr, 0) == 0 && val <= UINT64_MAX) { 443581a8a80SLaszlo Ersek if (*endptr == '\0') { 444eb7ee2cbSLaszlo Ersek *obj = val; 445eb7ee2cbSLaszlo Ersek processed(ov, name); 446eb7ee2cbSLaszlo Ersek return; 447eb7ee2cbSLaszlo Ersek } 448581a8a80SLaszlo Ersek if (*endptr == '-' && ov->list_mode == LM_IN_PROGRESS) { 449581a8a80SLaszlo Ersek unsigned long long val2; 450581a8a80SLaszlo Ersek 451581a8a80SLaszlo Ersek str = endptr + 1; 452581a8a80SLaszlo Ersek if (parse_uint_full(str, &val2, 0) == 0 && 45315a849beSLaszlo Ersek val2 <= UINT64_MAX && val <= val2 && 45415a849beSLaszlo Ersek val2 - val < OPTS_VISITOR_RANGE_MAX) { 455581a8a80SLaszlo Ersek ov->range_next.u = val; 456581a8a80SLaszlo Ersek ov->range_limit.u = val2; 457581a8a80SLaszlo Ersek ov->list_mode = LM_UNSIGNED_INTERVAL; 458581a8a80SLaszlo Ersek 459581a8a80SLaszlo Ersek /* as if entering on the top */ 460581a8a80SLaszlo Ersek *obj = ov->range_next.u; 461581a8a80SLaszlo Ersek return; 462581a8a80SLaszlo Ersek } 463581a8a80SLaszlo Ersek } 464581a8a80SLaszlo Ersek } 465c6bd8c70SMarkus Armbruster error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, 466581a8a80SLaszlo Ersek (ov->list_mode == LM_NONE) ? "a uint64 value" : 467581a8a80SLaszlo Ersek "a uint64 value or range"); 468eb7ee2cbSLaszlo Ersek } 469eb7ee2cbSLaszlo Ersek 470eb7ee2cbSLaszlo Ersek 471eb7ee2cbSLaszlo Ersek static void 4720b2a0d6bSEric Blake opts_type_size(Visitor *v, const char *name, uint64_t *obj, Error **errp) 473eb7ee2cbSLaszlo Ersek { 474d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 475eb7ee2cbSLaszlo Ersek const QemuOpt *opt; 476eb7ee2cbSLaszlo Ersek int64_t val; 477eb7ee2cbSLaszlo Ersek char *endptr; 478eb7ee2cbSLaszlo Ersek 479eb7ee2cbSLaszlo Ersek opt = lookup_scalar(ov, name, errp); 480eb7ee2cbSLaszlo Ersek if (!opt) { 481eb7ee2cbSLaszlo Ersek return; 482eb7ee2cbSLaszlo Ersek } 483eb7ee2cbSLaszlo Ersek 4844677bb40SMarc-André Lureau val = qemu_strtosz_suffix(opt->str ? opt->str : "", &endptr, 4854677bb40SMarc-André Lureau QEMU_STRTOSZ_DEFSUFFIX_B); 486cb45de67SAmos Kong if (val < 0 || *endptr) { 487c6bd8c70SMarkus Armbruster error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, 488eb7ee2cbSLaszlo Ersek "a size value representible as a non-negative int64"); 489cb45de67SAmos Kong return; 490cb45de67SAmos Kong } 491cb45de67SAmos Kong 492cb45de67SAmos Kong *obj = val; 493cb45de67SAmos Kong processed(ov, name); 494eb7ee2cbSLaszlo Ersek } 495eb7ee2cbSLaszlo Ersek 496eb7ee2cbSLaszlo Ersek 497eb7ee2cbSLaszlo Ersek static void 4980b2a0d6bSEric Blake opts_optional(Visitor *v, const char *name, bool *present) 499eb7ee2cbSLaszlo Ersek { 500d7bea75dSEric Blake OptsVisitor *ov = to_ov(v); 501eb7ee2cbSLaszlo Ersek 502eb7ee2cbSLaszlo Ersek /* we only support a single mandatory scalar field in a list node */ 503d9570434SLaszlo Ersek assert(ov->list_mode == LM_NONE); 504eb7ee2cbSLaszlo Ersek *present = (lookup_distinct(ov, name, NULL) != NULL); 505eb7ee2cbSLaszlo Ersek } 506eb7ee2cbSLaszlo Ersek 507eb7ee2cbSLaszlo Ersek 508eb7ee2cbSLaszlo Ersek OptsVisitor * 509eb7ee2cbSLaszlo Ersek opts_visitor_new(const QemuOpts *opts) 510eb7ee2cbSLaszlo Ersek { 511eb7ee2cbSLaszlo Ersek OptsVisitor *ov; 512eb7ee2cbSLaszlo Ersek 513eb7ee2cbSLaszlo Ersek ov = g_malloc0(sizeof *ov); 514eb7ee2cbSLaszlo Ersek 515*983f52d4SEric Blake ov->visitor.type = VISITOR_INPUT; 516*983f52d4SEric Blake 517eb7ee2cbSLaszlo Ersek ov->visitor.start_struct = &opts_start_struct; 518eb7ee2cbSLaszlo Ersek ov->visitor.end_struct = &opts_end_struct; 519eb7ee2cbSLaszlo Ersek 520eb7ee2cbSLaszlo Ersek ov->visitor.start_list = &opts_start_list; 521eb7ee2cbSLaszlo Ersek ov->visitor.next_list = &opts_next_list; 522eb7ee2cbSLaszlo Ersek ov->visitor.end_list = &opts_end_list; 523eb7ee2cbSLaszlo Ersek 5244c40314aSEric Blake ov->visitor.type_int64 = &opts_type_int64; 525eb7ee2cbSLaszlo Ersek ov->visitor.type_uint64 = &opts_type_uint64; 526eb7ee2cbSLaszlo Ersek ov->visitor.type_size = &opts_type_size; 527eb7ee2cbSLaszlo Ersek ov->visitor.type_bool = &opts_type_bool; 528eb7ee2cbSLaszlo Ersek ov->visitor.type_str = &opts_type_str; 529eb7ee2cbSLaszlo Ersek 530eb7ee2cbSLaszlo Ersek /* type_number() is not filled in, but this is not the first visitor to 531eb7ee2cbSLaszlo Ersek * skip some mandatory methods... */ 532eb7ee2cbSLaszlo Ersek 533e2cd0f4fSMarkus Armbruster ov->visitor.optional = &opts_optional; 534eb7ee2cbSLaszlo Ersek 535eb7ee2cbSLaszlo Ersek ov->opts_root = opts; 536eb7ee2cbSLaszlo Ersek 537eb7ee2cbSLaszlo Ersek return ov; 538eb7ee2cbSLaszlo Ersek } 539eb7ee2cbSLaszlo Ersek 540eb7ee2cbSLaszlo Ersek 541eb7ee2cbSLaszlo Ersek void 542eb7ee2cbSLaszlo Ersek opts_visitor_cleanup(OptsVisitor *ov) 543eb7ee2cbSLaszlo Ersek { 544eb7ee2cbSLaszlo Ersek if (ov->unprocessed_opts != NULL) { 545eb7ee2cbSLaszlo Ersek g_hash_table_destroy(ov->unprocessed_opts); 546eb7ee2cbSLaszlo Ersek } 547eb7ee2cbSLaszlo Ersek g_free(ov->fake_id_opt); 548e36c8766SStefan Weil g_free(ov); 549eb7ee2cbSLaszlo Ersek } 550eb7ee2cbSLaszlo Ersek 551eb7ee2cbSLaszlo Ersek 552eb7ee2cbSLaszlo Ersek Visitor * 553eb7ee2cbSLaszlo Ersek opts_get_visitor(OptsVisitor *ov) 554eb7ee2cbSLaszlo Ersek { 555eb7ee2cbSLaszlo Ersek return &ov->visitor; 556eb7ee2cbSLaszlo Ersek } 557