#include "qemu/help_option.h"
/*
- * Extracts the name of an option from the parameter string (p points at the
+ * Extracts the name of an option from the parameter string (@p points at the
* first byte of the option name)
*
- * The option name is delimited by delim (usually , or =) or the string end
- * and is copied into option. The caller is responsible for free'ing option
- * when no longer required.
+ * The option name is @len characters long and is copied into @option. The
+ * caller is responsible for free'ing @option when no longer required.
*
* The return value is the position of the delimiter/zero byte after the option
- * name in p.
+ * name in @p.
*/
-static const char *get_opt_name(const char *p, char **option, char delim)
+static const char *get_opt_name(const char *p, char **option, size_t len)
{
- char *offset = strchr(p, delim);
-
- if (offset) {
- *option = g_strndup(p, offset - p);
- return offset;
- } else {
- *option = g_strdup(p);
- return p + strlen(p);
- }
+ *option = g_strndup(p, len);
+ return p + len;
}
/*
return offset;
}
-static void parse_option_bool(const char *name, const char *value, bool *ret,
- Error **errp)
-{
- if (!strcmp(value, "on")) {
- *ret = 1;
- } else if (!strcmp(value, "off")) {
- *ret = 0;
- } else {
- error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
- name, "'on' or 'off'");
- }
-}
-
-static void parse_option_number(const char *name, const char *value,
+static bool parse_option_number(const char *name, const char *value,
uint64_t *ret, Error **errp)
{
uint64_t number;
if (err == -ERANGE) {
error_setg(errp, "Value '%s' is too large for parameter '%s'",
value, name);
- return;
+ return false;
}
if (err) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name, "a number");
- return;
+ return false;
}
*ret = number;
+ return true;
}
static const QemuOptDesc *find_desc_by_name(const QemuOptDesc *desc,
return desc ? desc->def_value_str : NULL;
}
-void parse_option_size(const char *name, const char *value,
+bool parse_option_size(const char *name, const char *value,
uint64_t *ret, Error **errp)
{
uint64_t size;
if (err == -ERANGE) {
error_setg(errp, "Value '%s' is out of range for parameter '%s'",
value, name);
- return;
+ return false;
}
if (err) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name,
error_append_hint(errp, "Optional suffix k, M, G, T, P or E means"
" kilo-, mega-, giga-, tera-, peta-\n"
"and exabytes, respectively.\n");
- return;
+ return false;
}
*ret = size;
+ return true;
}
static const char *opt_type_to_string(enum QemuOptType type)
if (opt == NULL) {
def_val = find_default_by_name(opts, name);
if (def_val) {
- parse_option_bool(name, def_val, &ret, &error_abort);
+ qapi_bool_parse(name, def_val, &ret, &error_abort);
}
return ret;
}
return qemu_opt_get_size_helper(opts, name, defval, true);
}
-static void qemu_opt_parse(QemuOpt *opt, Error **errp)
+static bool qemu_opt_parse(QemuOpt *opt, Error **errp)
{
if (opt->desc == NULL)
- return;
+ return true;
switch (opt->desc->type) {
case QEMU_OPT_STRING:
/* nothing */
- return;
+ return true;
case QEMU_OPT_BOOL:
- parse_option_bool(opt->name, opt->str, &opt->value.boolean, errp);
- break;
+ return qapi_bool_parse(opt->name, opt->str, &opt->value.boolean, errp);
case QEMU_OPT_NUMBER:
- parse_option_number(opt->name, opt->str, &opt->value.uint, errp);
- break;
+ return parse_option_number(opt->name, opt->str, &opt->value.uint,
+ errp);
case QEMU_OPT_SIZE:
- parse_option_size(opt->name, opt->str, &opt->value.uint, errp);
- break;
+ return parse_option_size(opt->name, opt->str, &opt->value.uint,
+ errp);
default:
abort();
}
}
-static bool opts_accepts_any(const QemuOpts *opts)
+static bool opts_accepts_any(const QemuOptsList *list)
{
- return opts->list->desc[0].name == NULL;
+ return list->desc[0].name == NULL;
}
int qemu_opt_unset(QemuOpts *opts, const char *name)
{
QemuOpt *opt = qemu_opt_find(opts, name);
- assert(opts_accepts_any(opts));
+ assert(opts_accepts_any(opts->list));
if (opt == NULL) {
return -1;
}
}
-static QemuOpt *opt_create(QemuOpts *opts, const char *name, char *value,
- bool prepend)
+static QemuOpt *opt_create(QemuOpts *opts, const char *name, char *value)
{
QemuOpt *opt = g_malloc0(sizeof(*opt));
opt->name = g_strdup(name);
opt->str = value;
opt->opts = opts;
- if (prepend) {
- QTAILQ_INSERT_HEAD(&opts->head, opt, next);
- } else {
- QTAILQ_INSERT_TAIL(&opts->head, opt, next);
- }
+ QTAILQ_INSERT_TAIL(&opts->head, opt, next);
return opt;
}
-static void opt_set(QemuOpts *opts, const char *name, char *value,
- bool prepend, bool *help_wanted, Error **errp)
+static bool opt_validate(QemuOpt *opt, Error **errp)
{
- QemuOpt *opt;
const QemuOptDesc *desc;
- Error *local_err = NULL;
+ const QemuOptsList *list = opt->opts->list;
- desc = find_desc_by_name(opts->list->desc, name);
- if (!desc && !opts_accepts_any(opts)) {
- g_free(value);
- error_setg(errp, QERR_INVALID_PARAMETER, name);
- if (help_wanted && is_help_option(name)) {
- *help_wanted = true;
- }
- return;
+ desc = find_desc_by_name(list->desc, opt->name);
+ if (!desc && !opts_accepts_any(list)) {
+ error_setg(errp, QERR_INVALID_PARAMETER, opt->name);
+ return false;
}
- opt = opt_create(opts, name, value, prepend);
opt->desc = desc;
- qemu_opt_parse(opt, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- qemu_opt_del(opt);
+ if (!qemu_opt_parse(opt, errp)) {
+ return false;
}
+
+ return true;
}
-void qemu_opt_set(QemuOpts *opts, const char *name, const char *value,
+bool qemu_opt_set(QemuOpts *opts, const char *name, const char *value,
Error **errp)
{
- opt_set(opts, name, g_strdup(value), false, NULL, errp);
+ QemuOpt *opt = opt_create(opts, name, g_strdup(value));
+
+ if (!opt_validate(opt, errp)) {
+ qemu_opt_del(opt);
+ return false;
+ }
+ return true;
}
-void qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val,
+bool qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val,
Error **errp)
{
QemuOpt *opt;
const QemuOptDesc *desc;
+ const QemuOptsList *list = opts->list;
- desc = find_desc_by_name(opts->list->desc, name);
- if (!desc && !opts_accepts_any(opts)) {
+ desc = find_desc_by_name(list->desc, name);
+ if (!desc && !opts_accepts_any(list)) {
error_setg(errp, QERR_INVALID_PARAMETER, name);
- return;
+ return false;
}
opt = g_malloc0(sizeof(*opt));
opt->value.boolean = !!val;
opt->str = g_strdup(val ? "on" : "off");
QTAILQ_INSERT_TAIL(&opts->head, opt, next);
+ return true;
}
-void qemu_opt_set_number(QemuOpts *opts, const char *name, int64_t val,
+bool qemu_opt_set_number(QemuOpts *opts, const char *name, int64_t val,
Error **errp)
{
QemuOpt *opt;
const QemuOptDesc *desc;
+ const QemuOptsList *list = opts->list;
- desc = find_desc_by_name(opts->list->desc, name);
- if (!desc && !opts_accepts_any(opts)) {
+ desc = find_desc_by_name(list->desc, name);
+ if (!desc && !opts_accepts_any(list)) {
error_setg(errp, QERR_INVALID_PARAMETER, name);
- return;
+ return false;
}
opt = g_malloc0(sizeof(*opt));
opt->value.uint = val;
opt->str = g_strdup_printf("%" PRId64, val);
QTAILQ_INSERT_TAIL(&opts->head, opt, next);
+ return true;
}
/**
{
QemuOpts *opts = NULL;
- if (id) {
+ if (list->merge_lists) {
+ if (id) {
+ error_setg(errp, QERR_INVALID_PARAMETER, "id");
+ return NULL;
+ }
+ opts = qemu_opts_find(list, NULL);
+ if (opts) {
+ return opts;
+ }
+ } else if (id) {
+ assert(fail_if_exists);
if (!id_wellformed(id)) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "id",
"an identifier");
}
opts = qemu_opts_find(list, id);
if (opts != NULL) {
- if (fail_if_exists && !list->merge_lists) {
- error_setg(errp, "Duplicate ID '%s' for %s", id, list->name);
- return NULL;
- } else {
- return opts;
- }
- }
- } else if (list->merge_lists) {
- opts = qemu_opts_find(list, NULL);
- if (opts) {
- return opts;
+ error_setg(errp, "Duplicate ID '%s' for %s", id, list->name);
+ return NULL;
}
}
opts = g_malloc0(sizeof(*opts));
loc_restore(&opts->loc);
}
-void qemu_opts_set(QemuOptsList *list, const char *id,
- const char *name, const char *value, Error **errp)
-{
- QemuOpts *opts;
-
- opts = qemu_opts_create(list, id, 1, errp);
- if (!opts) {
- return;
- }
- qemu_opt_set(opts, name, value, errp);
-}
-
const char *qemu_opts_id(QemuOpts *opts)
{
return opts->id;
static const char *get_opt_name_value(const char *params,
const char *firstname,
+ bool warn_on_flag,
+ bool *help_wanted,
char **name, char **value)
{
- const char *p, *pe, *pc;
-
- pe = strchr(params, '=');
- pc = strchr(params, ',');
+ const char *p;
+ const char *prefix = "";
+ size_t len;
+ bool is_help = false;
- if (!pe || (pc && pc < pe)) {
+ len = strcspn(params, "=,");
+ if (params[len] != '=') {
/* found "foo,more" */
if (firstname) {
/* implicitly named first option */
p = get_opt_value(params, value);
} else {
/* option without value, must be a flag */
- p = get_opt_name(params, name, ',');
+ p = get_opt_name(params, name, len);
if (strncmp(*name, "no", 2) == 0) {
memmove(*name, *name + 2, strlen(*name + 2) + 1);
*value = g_strdup("off");
+ prefix = "no";
} else {
*value = g_strdup("on");
+ is_help = is_help_option(*name);
+ }
+ if (!is_help && warn_on_flag) {
+ warn_report("short-form boolean option '%s%s' deprecated", prefix, *name);
+ if (g_str_equal(*name, "delay")) {
+ error_printf("Please use nodelay=%s instead\n", prefix[0] ? "on" : "off");
+ } else {
+ error_printf("Please use %s=%s instead\n", *name, *value);
+ }
}
}
} else {
/* found "foo=bar,more" */
- p = get_opt_name(params, name, '=');
+ p = get_opt_name(params, name, len);
assert(*p == '=');
p++;
p = get_opt_value(p, value);
}
assert(!*p || *p == ',');
+ if (help_wanted && is_help) {
+ *help_wanted = true;
+ }
if (*p == ',') {
p++;
}
return p;
}
-static void opts_do_parse(QemuOpts *opts, const char *params,
- const char *firstname, bool prepend,
- bool *help_wanted, Error **errp)
+static bool opts_do_parse(QemuOpts *opts, const char *params,
+ const char *firstname,
+ bool warn_on_flag, bool *help_wanted, Error **errp)
{
- Error *local_err = NULL;
char *option, *value;
const char *p;
+ QemuOpt *opt;
for (p = params; *p;) {
- p = get_opt_name_value(p, firstname, &option, &value);
+ p = get_opt_name_value(p, firstname, warn_on_flag, help_wanted, &option, &value);
+ if (help_wanted && *help_wanted) {
+ g_free(option);
+ g_free(value);
+ return false;
+ }
firstname = NULL;
if (!strcmp(option, "id")) {
continue;
}
- opt_set(opts, option, value, prepend, help_wanted, &local_err);
+ opt = opt_create(opts, option, value);
g_free(option);
- if (local_err) {
- error_propagate(errp, local_err);
- return;
+ if (!opt_validate(opt, errp)) {
+ qemu_opt_del(opt);
+ return false;
}
}
+
+ return true;
}
static char *opts_parse_id(const char *params)
char *name, *value;
for (p = params; *p;) {
- p = get_opt_name_value(p, NULL, &name, &value);
+ p = get_opt_name_value(p, NULL, false, NULL, &name, &value);
if (!strcmp(name, "id")) {
g_free(name);
return value;
{
const char *p;
char *name, *value;
- bool ret;
+ bool ret = false;
for (p = params; *p;) {
- p = get_opt_name_value(p, NULL, &name, &value);
- ret = is_help_option(name);
+ p = get_opt_name_value(p, NULL, false, &ret, &name, &value);
g_free(name);
g_free(value);
if (ret) {
* key=, and is treated as if key was @firstname.
* On error, store an error object through @errp if non-null.
*/
-void qemu_opts_do_parse(QemuOpts *opts, const char *params,
+bool qemu_opts_do_parse(QemuOpts *opts, const char *params,
const char *firstname, Error **errp)
{
- opts_do_parse(opts, params, firstname, false, NULL, errp);
+ return opts_do_parse(opts, params, firstname, false, NULL, errp);
}
static QemuOpts *opts_parse(QemuOptsList *list, const char *params,
- bool permit_abbrev, bool defaults,
- bool *help_wanted, Error **errp)
+ bool permit_abbrev,
+ bool warn_on_flag, bool *help_wanted, Error **errp)
{
const char *firstname;
char *id = opts_parse_id(params);
QemuOpts *opts;
- Error *local_err = NULL;
assert(!permit_abbrev || list->implied_opt_name);
firstname = permit_abbrev ? list->implied_opt_name : NULL;
- /*
- * This code doesn't work for defaults && !list->merge_lists: when
- * params has no id=, and list has an element with !opts->id, it
- * appends a new element instead of returning the existing opts.
- * However, we got no use for this case. Guard against possible
- * (if unlikely) future misuse:
- */
- assert(!defaults || list->merge_lists);
- opts = qemu_opts_create(list, id, !defaults, &local_err);
+ opts = qemu_opts_create(list, id, !list->merge_lists, errp);
g_free(id);
if (opts == NULL) {
- error_propagate(errp, local_err);
return NULL;
}
- opts_do_parse(opts, params, firstname, defaults, help_wanted, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ if (!opts_do_parse(opts, params, firstname,
+ warn_on_flag, help_wanted, errp)) {
qemu_opts_del(opts);
return NULL;
}
QemuOpts *opts;
bool help_wanted = false;
- opts = opts_parse(list, params, permit_abbrev, false, &help_wanted, &err);
- if (err) {
+ opts = opts_parse(list, params, permit_abbrev, true,
+ opts_accepts_any(list) ? NULL : &help_wanted,
+ &err);
+ if (!opts) {
+ assert(!!err + !!help_wanted == 1);
if (help_wanted) {
qemu_opts_print_help(list, true);
- error_free(err);
} else {
error_report_err(err);
}
return opts;
}
-void qemu_opts_set_defaults(QemuOptsList *list, const char *params,
- int permit_abbrev)
-{
- QemuOpts *opts;
-
- opts = opts_parse(list, params, permit_abbrev, true, NULL, NULL);
- assert(opts);
-}
-
-static void qemu_opts_from_qdict_entry(QemuOpts *opts,
+static bool qemu_opts_from_qdict_entry(QemuOpts *opts,
const QDictEntry *entry,
Error **errp)
{
const char *key = qdict_entry_key(entry);
QObject *obj = qdict_entry_value(entry);
- char buf[32], *tmp = NULL;
+ char buf[32];
+ g_autofree char *tmp = NULL;
const char *value;
if (!strcmp(key, "id")) {
- return;
+ return true;
}
switch (qobject_type(obj)) {
value = buf;
break;
default:
- return;
+ return true;
}
- qemu_opt_set(opts, key, value, errp);
- g_free(tmp);
+ return qemu_opt_set(opts, key, value, errp);
}
/*
QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict,
Error **errp)
{
- Error *local_err = NULL;
QemuOpts *opts;
const QDictEntry *entry;
return NULL;
}
- assert(opts != NULL);
-
for (entry = qdict_first(qdict);
entry;
entry = qdict_next(qdict, entry)) {
- qemu_opts_from_qdict_entry(opts, entry, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ if (!qemu_opts_from_qdict_entry(opts, entry, errp)) {
qemu_opts_del(opts);
return NULL;
}
* from the QDict. When this function returns, the QDict contains only those
* entries that couldn't be added to the QemuOpts.
*/
-void qemu_opts_absorb_qdict(QemuOpts *opts, QDict *qdict, Error **errp)
+bool qemu_opts_absorb_qdict(QemuOpts *opts, QDict *qdict, Error **errp)
{
const QDictEntry *entry, *next;
entry = qdict_first(qdict);
while (entry != NULL) {
- Error *local_err = NULL;
-
next = qdict_next(qdict, entry);
- if (find_desc_by_name(opts->list->desc, entry->key)) {
- qemu_opts_from_qdict_entry(opts, entry, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return;
+ if (opts_accepts_any(opts->list) ||
+ find_desc_by_name(opts->list->desc, entry->key)) {
+ if (!qemu_opts_from_qdict_entry(opts, entry, errp)) {
+ return false;
}
qdict_del(qdict, entry->key);
}
entry = next;
}
+
+ return true;
}
/*
/* Validate parsed opts against descriptions where no
* descriptions were provided in the QemuOptsList.
*/
-void qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc, Error **errp)
+bool qemu_opts_validate(QemuOpts *opts, const QemuOptDesc *desc, Error **errp)
{
QemuOpt *opt;
- Error *local_err = NULL;
- assert(opts_accepts_any(opts));
+ assert(opts_accepts_any(opts->list));
QTAILQ_FOREACH(opt, &opts->head, next) {
opt->desc = find_desc_by_name(desc, opt->name);
if (!opt->desc) {
error_setg(errp, QERR_INVALID_PARAMETER, opt->name);
- return;
+ return false;
}
- qemu_opt_parse(opt, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- return;
+ if (!qemu_opt_parse(opt, errp)) {
+ return false;
}
}
+
+ return true;
}
/**
void *opaque, Error **errp)
{
Location loc;
- QemuOpts *opts;
+ QemuOpts *opts, *next;
int rc = 0;
loc_push_none(&loc);
- QTAILQ_FOREACH(opts, &list->head, next) {
+ QTAILQ_FOREACH_SAFE(opts, &list->head, next, next) {
loc_restore(&opts->loc);
rc = func(opaque, opts, errp);
if (rc) {