1eb7ee2cbSLaszlo Ersek /* 2eb7ee2cbSLaszlo Ersek * Options Visitor 3eb7ee2cbSLaszlo Ersek * 4d9570434SLaszlo Ersek * Copyright Red Hat, Inc. 2012, 2013 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 1379ee7df8SPaolo Bonzini #include "qemu-common.h" 147b1b5d19SPaolo Bonzini #include "qapi/qmp/qerror.h" 157b1b5d19SPaolo Bonzini #include "qapi/opts-visitor.h" 161de7afc9SPaolo Bonzini #include "qemu/queue.h" 171de7afc9SPaolo Bonzini #include "qemu/option_int.h" 187b1b5d19SPaolo Bonzini #include "qapi/visitor-impl.h" 19eb7ee2cbSLaszlo Ersek 20eb7ee2cbSLaszlo Ersek 21d9570434SLaszlo Ersek enum ListMode 22d9570434SLaszlo Ersek { 23d9570434SLaszlo Ersek LM_NONE, /* not traversing a list of repeated options */ 24d9570434SLaszlo Ersek LM_STARTED, /* opts_start_list() succeeded */ 25d8754f40SLaszlo Ersek 26d8754f40SLaszlo Ersek LM_IN_PROGRESS, /* opts_next_list() has been called. 27d8754f40SLaszlo Ersek * 28d8754f40SLaszlo Ersek * Generating the next list link will consume the most 29d8754f40SLaszlo Ersek * recently parsed QemuOpt instance of the repeated 30d8754f40SLaszlo Ersek * option. 31d8754f40SLaszlo Ersek * 32d8754f40SLaszlo Ersek * Parsing a value into the list link will examine the 33d8754f40SLaszlo Ersek * next QemuOpt instance of the repeated option, and 34d8754f40SLaszlo Ersek * possibly enter LM_SIGNED_INTERVAL or 35d8754f40SLaszlo Ersek * LM_UNSIGNED_INTERVAL. 36d8754f40SLaszlo Ersek */ 37d8754f40SLaszlo Ersek 38d8754f40SLaszlo Ersek LM_SIGNED_INTERVAL, /* opts_next_list() has been called. 39d8754f40SLaszlo Ersek * 40d8754f40SLaszlo Ersek * Generating the next list link will consume the most 41d8754f40SLaszlo Ersek * recently stored element from the signed interval, 42d8754f40SLaszlo Ersek * parsed from the most recent QemuOpt instance of the 43d8754f40SLaszlo Ersek * repeated option. This may consume QemuOpt itself 44d8754f40SLaszlo Ersek * and return to LM_IN_PROGRESS. 45d8754f40SLaszlo Ersek * 46d8754f40SLaszlo Ersek * Parsing a value into the list link will store the 47d8754f40SLaszlo Ersek * next element of the signed interval. 48d8754f40SLaszlo Ersek */ 49d8754f40SLaszlo Ersek 50d8754f40SLaszlo Ersek LM_UNSIGNED_INTERVAL /* Same as above, only for an unsigned interval. */ 51d9570434SLaszlo Ersek }; 52d9570434SLaszlo Ersek 53d9570434SLaszlo Ersek typedef enum ListMode ListMode; 54d9570434SLaszlo Ersek 55eb7ee2cbSLaszlo Ersek struct OptsVisitor 56eb7ee2cbSLaszlo Ersek { 57eb7ee2cbSLaszlo Ersek Visitor visitor; 58eb7ee2cbSLaszlo Ersek 59eb7ee2cbSLaszlo Ersek /* Ownership remains with opts_visitor_new()'s caller. */ 60eb7ee2cbSLaszlo Ersek const QemuOpts *opts_root; 61eb7ee2cbSLaszlo Ersek 62eb7ee2cbSLaszlo Ersek unsigned depth; 63eb7ee2cbSLaszlo Ersek 64eb7ee2cbSLaszlo Ersek /* Non-null iff depth is positive. Each key is a QemuOpt name. Each value 65eb7ee2cbSLaszlo Ersek * is a non-empty GQueue, enumerating all QemuOpt occurrences with that 66eb7ee2cbSLaszlo Ersek * name. */ 67eb7ee2cbSLaszlo Ersek GHashTable *unprocessed_opts; 68eb7ee2cbSLaszlo Ersek 69eb7ee2cbSLaszlo Ersek /* The list currently being traversed with opts_start_list() / 70eb7ee2cbSLaszlo Ersek * opts_next_list(). The list must have a struct element type in the 71eb7ee2cbSLaszlo Ersek * schema, with a single mandatory scalar member. */ 72d9570434SLaszlo Ersek ListMode list_mode; 73eb7ee2cbSLaszlo Ersek GQueue *repeated_opts; 74eb7ee2cbSLaszlo Ersek 75d8754f40SLaszlo Ersek /* When parsing a list of repeating options as integers, values of the form 76d8754f40SLaszlo Ersek * "a-b", representing a closed interval, are allowed. Elements in the 77d8754f40SLaszlo Ersek * range are generated individually. 78d8754f40SLaszlo Ersek */ 79d8754f40SLaszlo Ersek union { 80d8754f40SLaszlo Ersek int64_t s; 81d8754f40SLaszlo Ersek uint64_t u; 82d8754f40SLaszlo Ersek } range_next, range_limit; 83d8754f40SLaszlo Ersek 84eb7ee2cbSLaszlo Ersek /* If "opts_root->id" is set, reinstantiate it as a fake QemuOpt for 85eb7ee2cbSLaszlo Ersek * uniformity. Only its "name" and "str" fields are set. "fake_id_opt" does 86eb7ee2cbSLaszlo Ersek * not survive or escape the OptsVisitor object. 87eb7ee2cbSLaszlo Ersek */ 88eb7ee2cbSLaszlo Ersek QemuOpt *fake_id_opt; 89eb7ee2cbSLaszlo Ersek }; 90eb7ee2cbSLaszlo Ersek 91eb7ee2cbSLaszlo Ersek 92eb7ee2cbSLaszlo Ersek static void 93eb7ee2cbSLaszlo Ersek destroy_list(gpointer list) 94eb7ee2cbSLaszlo Ersek { 95eb7ee2cbSLaszlo Ersek g_queue_free(list); 96eb7ee2cbSLaszlo Ersek } 97eb7ee2cbSLaszlo Ersek 98eb7ee2cbSLaszlo Ersek 99eb7ee2cbSLaszlo Ersek static void 100eb7ee2cbSLaszlo Ersek opts_visitor_insert(GHashTable *unprocessed_opts, const QemuOpt *opt) 101eb7ee2cbSLaszlo Ersek { 102eb7ee2cbSLaszlo Ersek GQueue *list; 103eb7ee2cbSLaszlo Ersek 104eb7ee2cbSLaszlo Ersek list = g_hash_table_lookup(unprocessed_opts, opt->name); 105eb7ee2cbSLaszlo Ersek if (list == NULL) { 106eb7ee2cbSLaszlo Ersek list = g_queue_new(); 107eb7ee2cbSLaszlo Ersek 108eb7ee2cbSLaszlo Ersek /* GHashTable will never try to free the keys -- we supply NULL as 109eb7ee2cbSLaszlo Ersek * "key_destroy_func" in opts_start_struct(). Thus cast away key 110eb7ee2cbSLaszlo Ersek * const-ness in order to suppress gcc's warning. 111eb7ee2cbSLaszlo Ersek */ 112eb7ee2cbSLaszlo Ersek g_hash_table_insert(unprocessed_opts, (gpointer)opt->name, list); 113eb7ee2cbSLaszlo Ersek } 114eb7ee2cbSLaszlo Ersek 115eb7ee2cbSLaszlo Ersek /* Similarly, destroy_list() doesn't call g_queue_free_full(). */ 116eb7ee2cbSLaszlo Ersek g_queue_push_tail(list, (gpointer)opt); 117eb7ee2cbSLaszlo Ersek } 118eb7ee2cbSLaszlo Ersek 119eb7ee2cbSLaszlo Ersek 120eb7ee2cbSLaszlo Ersek static void 121eb7ee2cbSLaszlo Ersek opts_start_struct(Visitor *v, void **obj, const char *kind, 122eb7ee2cbSLaszlo Ersek const char *name, size_t size, Error **errp) 123eb7ee2cbSLaszlo Ersek { 124eb7ee2cbSLaszlo Ersek OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); 125eb7ee2cbSLaszlo Ersek const QemuOpt *opt; 126eb7ee2cbSLaszlo Ersek 127*b7745397SMarkus Armbruster if (obj) { 128eb7ee2cbSLaszlo Ersek *obj = g_malloc0(size > 0 ? size : 1); 129*b7745397SMarkus Armbruster } 130eb7ee2cbSLaszlo Ersek if (ov->depth++ > 0) { 131eb7ee2cbSLaszlo Ersek return; 132eb7ee2cbSLaszlo Ersek } 133eb7ee2cbSLaszlo Ersek 134eb7ee2cbSLaszlo Ersek ov->unprocessed_opts = g_hash_table_new_full(&g_str_hash, &g_str_equal, 135eb7ee2cbSLaszlo Ersek NULL, &destroy_list); 136eb7ee2cbSLaszlo Ersek QTAILQ_FOREACH(opt, &ov->opts_root->head, next) { 137eb7ee2cbSLaszlo Ersek /* ensured by qemu-option.c::opts_do_parse() */ 138eb7ee2cbSLaszlo Ersek assert(strcmp(opt->name, "id") != 0); 139eb7ee2cbSLaszlo Ersek 140eb7ee2cbSLaszlo Ersek opts_visitor_insert(ov->unprocessed_opts, opt); 141eb7ee2cbSLaszlo Ersek } 142eb7ee2cbSLaszlo Ersek 143eb7ee2cbSLaszlo Ersek if (ov->opts_root->id != NULL) { 144eb7ee2cbSLaszlo Ersek ov->fake_id_opt = g_malloc0(sizeof *ov->fake_id_opt); 145eb7ee2cbSLaszlo Ersek 146eb7ee2cbSLaszlo Ersek ov->fake_id_opt->name = "id"; 147eb7ee2cbSLaszlo Ersek ov->fake_id_opt->str = ov->opts_root->id; 148eb7ee2cbSLaszlo Ersek opts_visitor_insert(ov->unprocessed_opts, ov->fake_id_opt); 149eb7ee2cbSLaszlo Ersek } 150eb7ee2cbSLaszlo Ersek } 151eb7ee2cbSLaszlo Ersek 152eb7ee2cbSLaszlo Ersek 153eb7ee2cbSLaszlo Ersek static gboolean 154eb7ee2cbSLaszlo Ersek ghr_true(gpointer ign_key, gpointer ign_value, gpointer ign_user_data) 155eb7ee2cbSLaszlo Ersek { 156eb7ee2cbSLaszlo Ersek return TRUE; 157eb7ee2cbSLaszlo Ersek } 158eb7ee2cbSLaszlo Ersek 159eb7ee2cbSLaszlo Ersek 160eb7ee2cbSLaszlo Ersek static void 161eb7ee2cbSLaszlo Ersek opts_end_struct(Visitor *v, Error **errp) 162eb7ee2cbSLaszlo Ersek { 163eb7ee2cbSLaszlo Ersek OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); 164eb7ee2cbSLaszlo Ersek GQueue *any; 165eb7ee2cbSLaszlo Ersek 166eb7ee2cbSLaszlo Ersek if (--ov->depth > 0) { 167eb7ee2cbSLaszlo Ersek return; 168eb7ee2cbSLaszlo Ersek } 169eb7ee2cbSLaszlo Ersek 170eb7ee2cbSLaszlo Ersek /* we should have processed all (distinct) QemuOpt instances */ 171eb7ee2cbSLaszlo Ersek any = g_hash_table_find(ov->unprocessed_opts, &ghr_true, NULL); 172eb7ee2cbSLaszlo Ersek if (any) { 173eb7ee2cbSLaszlo Ersek const QemuOpt *first; 174eb7ee2cbSLaszlo Ersek 175eb7ee2cbSLaszlo Ersek first = g_queue_peek_head(any); 176eb7ee2cbSLaszlo Ersek error_set(errp, QERR_INVALID_PARAMETER, first->name); 177eb7ee2cbSLaszlo Ersek } 178eb7ee2cbSLaszlo Ersek g_hash_table_destroy(ov->unprocessed_opts); 179eb7ee2cbSLaszlo Ersek ov->unprocessed_opts = NULL; 180eb7ee2cbSLaszlo Ersek g_free(ov->fake_id_opt); 181eb7ee2cbSLaszlo Ersek ov->fake_id_opt = NULL; 182eb7ee2cbSLaszlo Ersek } 183eb7ee2cbSLaszlo Ersek 184eb7ee2cbSLaszlo Ersek 185eb7ee2cbSLaszlo Ersek static GQueue * 186eb7ee2cbSLaszlo Ersek lookup_distinct(const OptsVisitor *ov, const char *name, Error **errp) 187eb7ee2cbSLaszlo Ersek { 188eb7ee2cbSLaszlo Ersek GQueue *list; 189eb7ee2cbSLaszlo Ersek 190eb7ee2cbSLaszlo Ersek list = g_hash_table_lookup(ov->unprocessed_opts, name); 191eb7ee2cbSLaszlo Ersek if (!list) { 192eb7ee2cbSLaszlo Ersek error_set(errp, QERR_MISSING_PARAMETER, name); 193eb7ee2cbSLaszlo Ersek } 194eb7ee2cbSLaszlo Ersek return list; 195eb7ee2cbSLaszlo Ersek } 196eb7ee2cbSLaszlo Ersek 197eb7ee2cbSLaszlo Ersek 198eb7ee2cbSLaszlo Ersek static void 199eb7ee2cbSLaszlo Ersek opts_start_list(Visitor *v, const char *name, Error **errp) 200eb7ee2cbSLaszlo Ersek { 201eb7ee2cbSLaszlo Ersek OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); 202eb7ee2cbSLaszlo Ersek 203eb7ee2cbSLaszlo Ersek /* we can't traverse a list in a list */ 204d9570434SLaszlo Ersek assert(ov->list_mode == LM_NONE); 205eb7ee2cbSLaszlo Ersek ov->repeated_opts = lookup_distinct(ov, name, errp); 206d9570434SLaszlo Ersek if (ov->repeated_opts != NULL) { 207d9570434SLaszlo Ersek ov->list_mode = LM_STARTED; 208d9570434SLaszlo Ersek } 209eb7ee2cbSLaszlo Ersek } 210eb7ee2cbSLaszlo Ersek 211eb7ee2cbSLaszlo Ersek 212eb7ee2cbSLaszlo Ersek static GenericList * 213eb7ee2cbSLaszlo Ersek opts_next_list(Visitor *v, GenericList **list, Error **errp) 214eb7ee2cbSLaszlo Ersek { 215eb7ee2cbSLaszlo Ersek OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); 216eb7ee2cbSLaszlo Ersek GenericList **link; 217eb7ee2cbSLaszlo Ersek 218d9570434SLaszlo Ersek switch (ov->list_mode) { 219d9570434SLaszlo Ersek case LM_STARTED: 220d9570434SLaszlo Ersek ov->list_mode = LM_IN_PROGRESS; 221eb7ee2cbSLaszlo Ersek link = list; 222d9570434SLaszlo Ersek break; 223d9570434SLaszlo Ersek 224d8754f40SLaszlo Ersek case LM_SIGNED_INTERVAL: 225d8754f40SLaszlo Ersek case LM_UNSIGNED_INTERVAL: 226d8754f40SLaszlo Ersek link = &(*list)->next; 227d8754f40SLaszlo Ersek 228d8754f40SLaszlo Ersek if (ov->list_mode == LM_SIGNED_INTERVAL) { 229d8754f40SLaszlo Ersek if (ov->range_next.s < ov->range_limit.s) { 230d8754f40SLaszlo Ersek ++ov->range_next.s; 231d8754f40SLaszlo Ersek break; 232d8754f40SLaszlo Ersek } 233d8754f40SLaszlo Ersek } else if (ov->range_next.u < ov->range_limit.u) { 234d8754f40SLaszlo Ersek ++ov->range_next.u; 235d8754f40SLaszlo Ersek break; 236d8754f40SLaszlo Ersek } 237d8754f40SLaszlo Ersek ov->list_mode = LM_IN_PROGRESS; 238d8754f40SLaszlo Ersek /* range has been completed, fall through in order to pop option */ 239d8754f40SLaszlo Ersek 240d9570434SLaszlo Ersek case LM_IN_PROGRESS: { 241eb7ee2cbSLaszlo Ersek const QemuOpt *opt; 242eb7ee2cbSLaszlo Ersek 243eb7ee2cbSLaszlo Ersek opt = g_queue_pop_head(ov->repeated_opts); 244eb7ee2cbSLaszlo Ersek if (g_queue_is_empty(ov->repeated_opts)) { 245eb7ee2cbSLaszlo Ersek g_hash_table_remove(ov->unprocessed_opts, opt->name); 246eb7ee2cbSLaszlo Ersek return NULL; 247eb7ee2cbSLaszlo Ersek } 248eb7ee2cbSLaszlo Ersek link = &(*list)->next; 249d9570434SLaszlo Ersek break; 250d9570434SLaszlo Ersek } 251d9570434SLaszlo Ersek 252d9570434SLaszlo Ersek default: 253d9570434SLaszlo Ersek abort(); 254eb7ee2cbSLaszlo Ersek } 255eb7ee2cbSLaszlo Ersek 256eb7ee2cbSLaszlo Ersek *link = g_malloc0(sizeof **link); 257eb7ee2cbSLaszlo Ersek return *link; 258eb7ee2cbSLaszlo Ersek } 259eb7ee2cbSLaszlo Ersek 260eb7ee2cbSLaszlo Ersek 261eb7ee2cbSLaszlo Ersek static void 262eb7ee2cbSLaszlo Ersek opts_end_list(Visitor *v, Error **errp) 263eb7ee2cbSLaszlo Ersek { 264eb7ee2cbSLaszlo Ersek OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); 265eb7ee2cbSLaszlo Ersek 266d8754f40SLaszlo Ersek assert(ov->list_mode == LM_STARTED || 267d8754f40SLaszlo Ersek ov->list_mode == LM_IN_PROGRESS || 268d8754f40SLaszlo Ersek ov->list_mode == LM_SIGNED_INTERVAL || 269d8754f40SLaszlo Ersek ov->list_mode == LM_UNSIGNED_INTERVAL); 270eb7ee2cbSLaszlo Ersek ov->repeated_opts = NULL; 271d9570434SLaszlo Ersek ov->list_mode = LM_NONE; 272eb7ee2cbSLaszlo Ersek } 273eb7ee2cbSLaszlo Ersek 274eb7ee2cbSLaszlo Ersek 275eb7ee2cbSLaszlo Ersek static const QemuOpt * 276eb7ee2cbSLaszlo Ersek lookup_scalar(const OptsVisitor *ov, const char *name, Error **errp) 277eb7ee2cbSLaszlo Ersek { 278d9570434SLaszlo Ersek if (ov->list_mode == LM_NONE) { 279eb7ee2cbSLaszlo Ersek GQueue *list; 280eb7ee2cbSLaszlo Ersek 281eb7ee2cbSLaszlo Ersek /* the last occurrence of any QemuOpt takes effect when queried by name 282eb7ee2cbSLaszlo Ersek */ 283eb7ee2cbSLaszlo Ersek list = lookup_distinct(ov, name, errp); 284eb7ee2cbSLaszlo Ersek return list ? g_queue_peek_tail(list) : NULL; 285eb7ee2cbSLaszlo Ersek } 286d9570434SLaszlo Ersek assert(ov->list_mode == LM_IN_PROGRESS); 287eb7ee2cbSLaszlo Ersek return g_queue_peek_head(ov->repeated_opts); 288eb7ee2cbSLaszlo Ersek } 289eb7ee2cbSLaszlo Ersek 290eb7ee2cbSLaszlo Ersek 291eb7ee2cbSLaszlo Ersek static void 292eb7ee2cbSLaszlo Ersek processed(OptsVisitor *ov, const char *name) 293eb7ee2cbSLaszlo Ersek { 294d9570434SLaszlo Ersek if (ov->list_mode == LM_NONE) { 295eb7ee2cbSLaszlo Ersek g_hash_table_remove(ov->unprocessed_opts, name); 296d9570434SLaszlo Ersek return; 297eb7ee2cbSLaszlo Ersek } 298d9570434SLaszlo Ersek assert(ov->list_mode == LM_IN_PROGRESS); 299d9570434SLaszlo Ersek /* do nothing */ 300eb7ee2cbSLaszlo Ersek } 301eb7ee2cbSLaszlo Ersek 302eb7ee2cbSLaszlo Ersek 303eb7ee2cbSLaszlo Ersek static void 304eb7ee2cbSLaszlo Ersek opts_type_str(Visitor *v, char **obj, const char *name, Error **errp) 305eb7ee2cbSLaszlo Ersek { 306eb7ee2cbSLaszlo Ersek OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); 307eb7ee2cbSLaszlo Ersek const QemuOpt *opt; 308eb7ee2cbSLaszlo Ersek 309eb7ee2cbSLaszlo Ersek opt = lookup_scalar(ov, name, errp); 310eb7ee2cbSLaszlo Ersek if (!opt) { 311eb7ee2cbSLaszlo Ersek return; 312eb7ee2cbSLaszlo Ersek } 313eb7ee2cbSLaszlo Ersek *obj = g_strdup(opt->str ? opt->str : ""); 314eb7ee2cbSLaszlo Ersek processed(ov, name); 315eb7ee2cbSLaszlo Ersek } 316eb7ee2cbSLaszlo Ersek 317eb7ee2cbSLaszlo Ersek 318eb7ee2cbSLaszlo Ersek /* mimics qemu-option.c::parse_option_bool() */ 319eb7ee2cbSLaszlo Ersek static void 320eb7ee2cbSLaszlo Ersek opts_type_bool(Visitor *v, bool *obj, const char *name, Error **errp) 321eb7ee2cbSLaszlo Ersek { 322eb7ee2cbSLaszlo Ersek OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); 323eb7ee2cbSLaszlo Ersek const QemuOpt *opt; 324eb7ee2cbSLaszlo Ersek 325eb7ee2cbSLaszlo Ersek opt = lookup_scalar(ov, name, errp); 326eb7ee2cbSLaszlo Ersek if (!opt) { 327eb7ee2cbSLaszlo Ersek return; 328eb7ee2cbSLaszlo Ersek } 329eb7ee2cbSLaszlo Ersek 330eb7ee2cbSLaszlo Ersek if (opt->str) { 331eb7ee2cbSLaszlo Ersek if (strcmp(opt->str, "on") == 0 || 332eb7ee2cbSLaszlo Ersek strcmp(opt->str, "yes") == 0 || 333eb7ee2cbSLaszlo Ersek strcmp(opt->str, "y") == 0) { 334eb7ee2cbSLaszlo Ersek *obj = true; 335eb7ee2cbSLaszlo Ersek } else if (strcmp(opt->str, "off") == 0 || 336eb7ee2cbSLaszlo Ersek strcmp(opt->str, "no") == 0 || 337eb7ee2cbSLaszlo Ersek strcmp(opt->str, "n") == 0) { 338eb7ee2cbSLaszlo Ersek *obj = false; 339eb7ee2cbSLaszlo Ersek } else { 340eb7ee2cbSLaszlo Ersek error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, 341eb7ee2cbSLaszlo Ersek "on|yes|y|off|no|n"); 342eb7ee2cbSLaszlo Ersek return; 343eb7ee2cbSLaszlo Ersek } 344eb7ee2cbSLaszlo Ersek } else { 345eb7ee2cbSLaszlo Ersek *obj = true; 346eb7ee2cbSLaszlo Ersek } 347eb7ee2cbSLaszlo Ersek 348eb7ee2cbSLaszlo Ersek processed(ov, name); 349eb7ee2cbSLaszlo Ersek } 350eb7ee2cbSLaszlo Ersek 351eb7ee2cbSLaszlo Ersek 352eb7ee2cbSLaszlo Ersek static void 353eb7ee2cbSLaszlo Ersek opts_type_int(Visitor *v, int64_t *obj, const char *name, Error **errp) 354eb7ee2cbSLaszlo Ersek { 355eb7ee2cbSLaszlo Ersek OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); 356eb7ee2cbSLaszlo Ersek const QemuOpt *opt; 357eb7ee2cbSLaszlo Ersek const char *str; 358eb7ee2cbSLaszlo Ersek long long val; 359eb7ee2cbSLaszlo Ersek char *endptr; 360eb7ee2cbSLaszlo Ersek 361d8754f40SLaszlo Ersek if (ov->list_mode == LM_SIGNED_INTERVAL) { 362d8754f40SLaszlo Ersek *obj = ov->range_next.s; 363d8754f40SLaszlo Ersek return; 364d8754f40SLaszlo Ersek } 365d8754f40SLaszlo Ersek 366eb7ee2cbSLaszlo Ersek opt = lookup_scalar(ov, name, errp); 367eb7ee2cbSLaszlo Ersek if (!opt) { 368eb7ee2cbSLaszlo Ersek return; 369eb7ee2cbSLaszlo Ersek } 370eb7ee2cbSLaszlo Ersek str = opt->str ? opt->str : ""; 371eb7ee2cbSLaszlo Ersek 3721e1c555aSLaszlo Ersek /* we've gotten past lookup_scalar() */ 3731e1c555aSLaszlo Ersek assert(ov->list_mode == LM_NONE || ov->list_mode == LM_IN_PROGRESS); 3741e1c555aSLaszlo Ersek 375eb7ee2cbSLaszlo Ersek errno = 0; 376eb7ee2cbSLaszlo Ersek val = strtoll(str, &endptr, 0); 3771e1c555aSLaszlo Ersek if (errno == 0 && endptr > str && INT64_MIN <= val && val <= INT64_MAX) { 3781e1c555aSLaszlo Ersek if (*endptr == '\0') { 379eb7ee2cbSLaszlo Ersek *obj = val; 380eb7ee2cbSLaszlo Ersek processed(ov, name); 381eb7ee2cbSLaszlo Ersek return; 382eb7ee2cbSLaszlo Ersek } 3831e1c555aSLaszlo Ersek if (*endptr == '-' && ov->list_mode == LM_IN_PROGRESS) { 3841e1c555aSLaszlo Ersek long long val2; 3851e1c555aSLaszlo Ersek 3861e1c555aSLaszlo Ersek str = endptr + 1; 3871e1c555aSLaszlo Ersek val2 = strtoll(str, &endptr, 0); 3881e1c555aSLaszlo Ersek if (errno == 0 && endptr > str && *endptr == '\0' && 38915a849beSLaszlo Ersek INT64_MIN <= val2 && val2 <= INT64_MAX && val <= val2 && 39015a849beSLaszlo Ersek (val > INT64_MAX - OPTS_VISITOR_RANGE_MAX || 39115a849beSLaszlo Ersek val2 < val + OPTS_VISITOR_RANGE_MAX)) { 3921e1c555aSLaszlo Ersek ov->range_next.s = val; 3931e1c555aSLaszlo Ersek ov->range_limit.s = val2; 3941e1c555aSLaszlo Ersek ov->list_mode = LM_SIGNED_INTERVAL; 3951e1c555aSLaszlo Ersek 3961e1c555aSLaszlo Ersek /* as if entering on the top */ 3971e1c555aSLaszlo Ersek *obj = ov->range_next.s; 3981e1c555aSLaszlo Ersek return; 3991e1c555aSLaszlo Ersek } 4001e1c555aSLaszlo Ersek } 4011e1c555aSLaszlo Ersek } 4021e1c555aSLaszlo Ersek error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, 4031e1c555aSLaszlo Ersek (ov->list_mode == LM_NONE) ? "an int64 value" : 4041e1c555aSLaszlo Ersek "an int64 value or range"); 405eb7ee2cbSLaszlo Ersek } 406eb7ee2cbSLaszlo Ersek 407eb7ee2cbSLaszlo Ersek 408eb7ee2cbSLaszlo Ersek static void 409eb7ee2cbSLaszlo Ersek opts_type_uint64(Visitor *v, uint64_t *obj, const char *name, Error **errp) 410eb7ee2cbSLaszlo Ersek { 411eb7ee2cbSLaszlo Ersek OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); 412eb7ee2cbSLaszlo Ersek const QemuOpt *opt; 413eb7ee2cbSLaszlo Ersek const char *str; 41462d090e2SLaszlo Ersek unsigned long long val; 415581a8a80SLaszlo Ersek char *endptr; 416eb7ee2cbSLaszlo Ersek 417d8754f40SLaszlo Ersek if (ov->list_mode == LM_UNSIGNED_INTERVAL) { 418d8754f40SLaszlo Ersek *obj = ov->range_next.u; 419d8754f40SLaszlo Ersek return; 420d8754f40SLaszlo Ersek } 421d8754f40SLaszlo Ersek 422eb7ee2cbSLaszlo Ersek opt = lookup_scalar(ov, name, errp); 423eb7ee2cbSLaszlo Ersek if (!opt) { 424eb7ee2cbSLaszlo Ersek return; 425eb7ee2cbSLaszlo Ersek } 426eb7ee2cbSLaszlo Ersek str = opt->str; 427eb7ee2cbSLaszlo Ersek 428581a8a80SLaszlo Ersek /* we've gotten past lookup_scalar() */ 429581a8a80SLaszlo Ersek assert(ov->list_mode == LM_NONE || ov->list_mode == LM_IN_PROGRESS); 430581a8a80SLaszlo Ersek 431581a8a80SLaszlo Ersek if (parse_uint(str, &val, &endptr, 0) == 0 && val <= UINT64_MAX) { 432581a8a80SLaszlo Ersek if (*endptr == '\0') { 433eb7ee2cbSLaszlo Ersek *obj = val; 434eb7ee2cbSLaszlo Ersek processed(ov, name); 435eb7ee2cbSLaszlo Ersek return; 436eb7ee2cbSLaszlo Ersek } 437581a8a80SLaszlo Ersek if (*endptr == '-' && ov->list_mode == LM_IN_PROGRESS) { 438581a8a80SLaszlo Ersek unsigned long long val2; 439581a8a80SLaszlo Ersek 440581a8a80SLaszlo Ersek str = endptr + 1; 441581a8a80SLaszlo Ersek if (parse_uint_full(str, &val2, 0) == 0 && 44215a849beSLaszlo Ersek val2 <= UINT64_MAX && val <= val2 && 44315a849beSLaszlo Ersek val2 - val < OPTS_VISITOR_RANGE_MAX) { 444581a8a80SLaszlo Ersek ov->range_next.u = val; 445581a8a80SLaszlo Ersek ov->range_limit.u = val2; 446581a8a80SLaszlo Ersek ov->list_mode = LM_UNSIGNED_INTERVAL; 447581a8a80SLaszlo Ersek 448581a8a80SLaszlo Ersek /* as if entering on the top */ 449581a8a80SLaszlo Ersek *obj = ov->range_next.u; 450581a8a80SLaszlo Ersek return; 451581a8a80SLaszlo Ersek } 452581a8a80SLaszlo Ersek } 453581a8a80SLaszlo Ersek } 454eb7ee2cbSLaszlo Ersek error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, 455581a8a80SLaszlo Ersek (ov->list_mode == LM_NONE) ? "a uint64 value" : 456581a8a80SLaszlo Ersek "a uint64 value or range"); 457eb7ee2cbSLaszlo Ersek } 458eb7ee2cbSLaszlo Ersek 459eb7ee2cbSLaszlo Ersek 460eb7ee2cbSLaszlo Ersek static void 461eb7ee2cbSLaszlo Ersek opts_type_size(Visitor *v, uint64_t *obj, const char *name, Error **errp) 462eb7ee2cbSLaszlo Ersek { 463eb7ee2cbSLaszlo Ersek OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); 464eb7ee2cbSLaszlo Ersek const QemuOpt *opt; 465eb7ee2cbSLaszlo Ersek int64_t val; 466eb7ee2cbSLaszlo Ersek char *endptr; 467eb7ee2cbSLaszlo Ersek 468eb7ee2cbSLaszlo Ersek opt = lookup_scalar(ov, name, errp); 469eb7ee2cbSLaszlo Ersek if (!opt) { 470eb7ee2cbSLaszlo Ersek return; 471eb7ee2cbSLaszlo Ersek } 472eb7ee2cbSLaszlo Ersek 473eb7ee2cbSLaszlo Ersek val = strtosz_suffix(opt->str ? opt->str : "", &endptr, 474eb7ee2cbSLaszlo Ersek STRTOSZ_DEFSUFFIX_B); 475eb7ee2cbSLaszlo Ersek if (val != -1 && *endptr == '\0') { 476eb7ee2cbSLaszlo Ersek *obj = val; 477eb7ee2cbSLaszlo Ersek processed(ov, name); 478eb7ee2cbSLaszlo Ersek return; 479eb7ee2cbSLaszlo Ersek } 480eb7ee2cbSLaszlo Ersek error_set(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, 481eb7ee2cbSLaszlo Ersek "a size value representible as a non-negative int64"); 482eb7ee2cbSLaszlo Ersek } 483eb7ee2cbSLaszlo Ersek 484eb7ee2cbSLaszlo Ersek 485eb7ee2cbSLaszlo Ersek static void 486eb7ee2cbSLaszlo Ersek opts_start_optional(Visitor *v, bool *present, const char *name, 487eb7ee2cbSLaszlo Ersek Error **errp) 488eb7ee2cbSLaszlo Ersek { 489eb7ee2cbSLaszlo Ersek OptsVisitor *ov = DO_UPCAST(OptsVisitor, visitor, v); 490eb7ee2cbSLaszlo Ersek 491eb7ee2cbSLaszlo Ersek /* we only support a single mandatory scalar field in a list node */ 492d9570434SLaszlo Ersek assert(ov->list_mode == LM_NONE); 493eb7ee2cbSLaszlo Ersek *present = (lookup_distinct(ov, name, NULL) != NULL); 494eb7ee2cbSLaszlo Ersek } 495eb7ee2cbSLaszlo Ersek 496eb7ee2cbSLaszlo Ersek 497eb7ee2cbSLaszlo Ersek OptsVisitor * 498eb7ee2cbSLaszlo Ersek opts_visitor_new(const QemuOpts *opts) 499eb7ee2cbSLaszlo Ersek { 500eb7ee2cbSLaszlo Ersek OptsVisitor *ov; 501eb7ee2cbSLaszlo Ersek 502eb7ee2cbSLaszlo Ersek ov = g_malloc0(sizeof *ov); 503eb7ee2cbSLaszlo Ersek 504eb7ee2cbSLaszlo Ersek ov->visitor.start_struct = &opts_start_struct; 505eb7ee2cbSLaszlo Ersek ov->visitor.end_struct = &opts_end_struct; 506eb7ee2cbSLaszlo Ersek 507eb7ee2cbSLaszlo Ersek ov->visitor.start_list = &opts_start_list; 508eb7ee2cbSLaszlo Ersek ov->visitor.next_list = &opts_next_list; 509eb7ee2cbSLaszlo Ersek ov->visitor.end_list = &opts_end_list; 510eb7ee2cbSLaszlo Ersek 511eb7ee2cbSLaszlo Ersek /* input_type_enum() covers both "normal" enums and union discriminators. 512eb7ee2cbSLaszlo Ersek * The union discriminator field is always generated as "type"; it should 513eb7ee2cbSLaszlo Ersek * match the "type" QemuOpt child of any QemuOpts. 514eb7ee2cbSLaszlo Ersek * 515eb7ee2cbSLaszlo Ersek * input_type_enum() will remove the looked-up key from the 516eb7ee2cbSLaszlo Ersek * "unprocessed_opts" hash even if the lookup fails, because the removal is 517eb7ee2cbSLaszlo Ersek * done earlier in opts_type_str(). This should be harmless. 518eb7ee2cbSLaszlo Ersek */ 519eb7ee2cbSLaszlo Ersek ov->visitor.type_enum = &input_type_enum; 520eb7ee2cbSLaszlo Ersek 521eb7ee2cbSLaszlo Ersek ov->visitor.type_int = &opts_type_int; 522eb7ee2cbSLaszlo Ersek ov->visitor.type_uint64 = &opts_type_uint64; 523eb7ee2cbSLaszlo Ersek ov->visitor.type_size = &opts_type_size; 524eb7ee2cbSLaszlo Ersek ov->visitor.type_bool = &opts_type_bool; 525eb7ee2cbSLaszlo Ersek ov->visitor.type_str = &opts_type_str; 526eb7ee2cbSLaszlo Ersek 527eb7ee2cbSLaszlo Ersek /* type_number() is not filled in, but this is not the first visitor to 528eb7ee2cbSLaszlo Ersek * skip some mandatory methods... */ 529eb7ee2cbSLaszlo Ersek 530eb7ee2cbSLaszlo Ersek ov->visitor.start_optional = &opts_start_optional; 531eb7ee2cbSLaszlo Ersek 532eb7ee2cbSLaszlo Ersek ov->opts_root = opts; 533eb7ee2cbSLaszlo Ersek 534eb7ee2cbSLaszlo Ersek return ov; 535eb7ee2cbSLaszlo Ersek } 536eb7ee2cbSLaszlo Ersek 537eb7ee2cbSLaszlo Ersek 538eb7ee2cbSLaszlo Ersek void 539eb7ee2cbSLaszlo Ersek opts_visitor_cleanup(OptsVisitor *ov) 540eb7ee2cbSLaszlo Ersek { 541eb7ee2cbSLaszlo Ersek if (ov->unprocessed_opts != NULL) { 542eb7ee2cbSLaszlo Ersek g_hash_table_destroy(ov->unprocessed_opts); 543eb7ee2cbSLaszlo Ersek } 544eb7ee2cbSLaszlo Ersek g_free(ov->fake_id_opt); 545e36c8766SStefan Weil g_free(ov); 546eb7ee2cbSLaszlo Ersek } 547eb7ee2cbSLaszlo Ersek 548eb7ee2cbSLaszlo Ersek 549eb7ee2cbSLaszlo Ersek Visitor * 550eb7ee2cbSLaszlo Ersek opts_get_visitor(OptsVisitor *ov) 551eb7ee2cbSLaszlo Ersek { 552eb7ee2cbSLaszlo Ersek return &ov->visitor; 553eb7ee2cbSLaszlo Ersek } 554