From 2910f183ddd5286911bc1e3499ea93cb57de8b75 Mon Sep 17 00:00:00 2001 From: David 'Digit' Turner Date: Mon, 10 May 2010 18:48:35 -0700 Subject: Upstream: Misc integration - includes qobject.h and related sources Change-Id: Idfa93ab5c67c95a3bc1869eeaf3a84a75fe24cd6 --- qemu-option.c | 542 +++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 501 insertions(+), 41 deletions(-) (limited to 'qemu-option.c') diff --git a/qemu-option.c b/qemu-option.c index 646bbad..738e5b6 100644 --- a/qemu-option.c +++ b/qemu-option.c @@ -85,6 +85,70 @@ const char *get_opt_value(char *buf, int buf_size, const char *p) return p; } +int get_next_param_value(char *buf, int buf_size, + const char *tag, const char **pstr) +{ + const char *p; + char option[128]; + + p = *pstr; + for(;;) { + p = get_opt_name(option, sizeof(option), p, '='); + if (*p != '=') + break; + p++; + if (!strcmp(tag, option)) { + *pstr = get_opt_value(buf, buf_size, p); + if (**pstr == ',') { + (*pstr)++; + } + return strlen(buf); + } else { + p = get_opt_value(NULL, 0, p); + } + if (*p != ',') + break; + p++; + } + return 0; +} + +int get_param_value(char *buf, int buf_size, + const char *tag, const char *str) +{ + return get_next_param_value(buf, buf_size, tag, &str); +} + +int check_params(char *buf, int buf_size, + const char * const *params, const char *str) +{ + const char *p; + int i; + + p = str; + while (*p != '\0') { + p = get_opt_name(buf, buf_size, p, '='); + if (*p != '=') { + return -1; + } + p++; + for (i = 0; params[i] != NULL; i++) { + if (!strcmp(params[i], buf)) { + break; + } + } + if (params[i] == NULL) { + return -1; + } + p = get_opt_value(NULL, 0, p); + if (*p != ',') { + break; + } + p++; + } + return 0; +} + /* * Searches an option list for an option with the given name */ @@ -101,6 +165,76 @@ QEMUOptionParameter *get_option_parameter(QEMUOptionParameter *list, return NULL; } +static int parse_option_bool(const char *name, const char *value, int *ret) +{ + if (value != NULL) { + if (!strcmp(value, "on")) { + *ret = 1; + } else if (!strcmp(value, "off")) { + *ret = 0; + } else { + fprintf(stderr, "Option '%s': Use 'on' or 'off'\n", name); + return -1; + } + } else { + *ret = 1; + } + return 0; +} + +static int parse_option_number(const char *name, const char *value, uint64_t *ret) +{ + char *postfix; + uint64_t number; + + if (value != NULL) { + number = strtoull(value, &postfix, 0); + if (*postfix != '\0') { + fprintf(stderr, "Option '%s' needs a number as parameter\n", name); + return -1; + } + *ret = number; + } else { + fprintf(stderr, "Option '%s' needs a parameter\n", name); + return -1; + } + return 0; +} + +static int parse_option_size(const char *name, const char *value, uint64_t *ret) +{ + char *postfix; + double sizef; + + if (value != NULL) { + sizef = strtod(value, &postfix); + switch (*postfix) { + case 'T': + sizef *= 1024; + case 'G': + sizef *= 1024; + case 'M': + sizef *= 1024; + case 'K': + case 'k': + sizef *= 1024; + case 'b': + case '\0': + *ret = (uint64_t) sizef; + break; + default: + fprintf(stderr, "Option '%s' needs size as parameter\n", name); + fprintf(stderr, "You may use k, M, G or T suffixes for " + "kilobytes, megabytes, gigabytes and terabytes.\n"); + return -1; + } + } else { + fprintf(stderr, "Option '%s' needs a parameter\n", name); + return -1; + } + return 0; +} + /* * Sets the value of a parameter in a given option list. The parsing of the * value depends on the type of option: @@ -121,6 +255,8 @@ QEMUOptionParameter *get_option_parameter(QEMUOptionParameter *list, int set_option_parameter(QEMUOptionParameter *list, const char *name, const char *value) { + int flag; + // Find a matching parameter list = get_option_parameter(list, name); if (list == NULL) { @@ -131,23 +267,14 @@ int set_option_parameter(QEMUOptionParameter *list, const char *name, // Process parameter switch (list->type) { case OPT_FLAG: - if (value != NULL) { - if (!strcmp(value, "on")) { - list->value.n = 1; - } else if (!strcmp(value, "off")) { - list->value.n = 0; - } else { - fprintf(stderr, "Option '%s': Use 'on' or 'off'\n", name); - return -1; - } - } else { - list->value.n = 1; - } + if (parse_option_bool(name, value, &flag) == -1) + return -1; + list->value.n = flag; break; case OPT_STRING: if (value != NULL) { - list->value.s = strdup(value); + list->value.s = qemu_strdup(value); } else { fprintf(stderr, "Option '%s' needs a parameter\n", name); return -1; @@ -155,34 +282,10 @@ int set_option_parameter(QEMUOptionParameter *list, const char *name, break; case OPT_SIZE: - if (value != NULL) { - double sizef = strtod(value, (char**) &value); - - switch (*value) { - case 'T': - sizef *= 1024; - case 'G': - sizef *= 1024; - case 'M': - sizef *= 1024; - case 'K': - case 'k': - sizef *= 1024; - case 'b': - case '\0': - list->value.n = (uint64_t) sizef; - break; - default: - fprintf(stderr, "Option '%s' needs size as parameter\n", name); - fprintf(stderr, "You may use k, M, G or T suffixes for " - "kilobytes, megabytes, gigabytes and terabytes.\n"); - return -1; - } - } else { - fprintf(stderr, "Option '%s' needs a parameter\n", name); + if (parse_option_size(name, value, &list->value.n) == -1) return -1; - } break; + default: fprintf(stderr, "Bug: Option '%s' has an unknown type\n", name); return -1; @@ -231,12 +334,12 @@ void free_option_parameters(QEMUOptionParameter *list) while (cur && cur->name) { if (cur->type == OPT_STRING) { - free(cur->value.s); + qemu_free(cur->value.s); } cur++; } - free(list); + qemu_free(list); } /* @@ -360,3 +463,360 @@ void print_option_help(QEMUOptionParameter *list) list++; } } + +/* ------------------------------------------------------------------ */ + +struct QemuOpt { + const char *name; + const char *str; + + QemuOptDesc *desc; + union { + int boolean; + uint64_t uint; + } value; + + QemuOpts *opts; + QTAILQ_ENTRY(QemuOpt) next; +}; + +struct QemuOpts { + char *id; + QemuOptsList *list; + QTAILQ_HEAD(QemuOptHead, QemuOpt) head; + QTAILQ_ENTRY(QemuOpts) next; +}; + +static QemuOpt *qemu_opt_find(QemuOpts *opts, const char *name) +{ + QemuOpt *opt; + + QTAILQ_FOREACH_REVERSE(opt, &opts->head, QemuOptHead, next) { + if (strcmp(opt->name, name) != 0) + continue; + return opt; + } + return NULL; +} + +const char *qemu_opt_get(QemuOpts *opts, const char *name) +{ + QemuOpt *opt = qemu_opt_find(opts, name); + return opt ? opt->str : NULL; +} + +int qemu_opt_get_bool(QemuOpts *opts, const char *name, int defval) +{ + QemuOpt *opt = qemu_opt_find(opts, name); + + if (opt == NULL) + return defval; + assert(opt->desc && opt->desc->type == QEMU_OPT_BOOL); + return opt->value.boolean; +} + +uint64_t qemu_opt_get_number(QemuOpts *opts, const char *name, uint64_t defval) +{ + QemuOpt *opt = qemu_opt_find(opts, name); + + if (opt == NULL) + return defval; + assert(opt->desc && opt->desc->type == QEMU_OPT_NUMBER); + return opt->value.uint; +} + +uint64_t qemu_opt_get_size(QemuOpts *opts, const char *name, uint64_t defval) +{ + QemuOpt *opt = qemu_opt_find(opts, name); + + if (opt == NULL) + return defval; + assert(opt->desc && opt->desc->type == QEMU_OPT_SIZE); + return opt->value.uint; +} + +static int qemu_opt_parse(QemuOpt *opt) +{ + if (opt->desc == NULL) + return 0; + switch (opt->desc->type) { + case QEMU_OPT_STRING: + /* nothing */ + return 0; + case QEMU_OPT_BOOL: + return parse_option_bool(opt->name, opt->str, &opt->value.boolean); + case QEMU_OPT_NUMBER: + return parse_option_number(opt->name, opt->str, &opt->value.uint); + case QEMU_OPT_SIZE: + return parse_option_size(opt->name, opt->str, &opt->value.uint); + default: + abort(); + } +} + +static void qemu_opt_del(QemuOpt *opt) +{ + QTAILQ_REMOVE(&opt->opts->head, opt, next); + qemu_free((/* !const */ char*)opt->name); + qemu_free((/* !const */ char*)opt->str); + qemu_free(opt); +} + +int qemu_opt_set(QemuOpts *opts, const char *name, const char *value) +{ + QemuOpt *opt; + QemuOptDesc *desc = opts->list->desc; + int i; + + for (i = 0; desc[i].name != NULL; i++) { + if (strcmp(desc[i].name, name) == 0) { + break; + } + } + if (desc[i].name == NULL) { + if (i == 0) { + /* empty list -> allow any */; + } else { + fprintf(stderr, "option \"%s\" is not valid for %s\n", + name, opts->list->name); + return -1; + } + } + + opt = qemu_mallocz(sizeof(*opt)); + opt->name = qemu_strdup(name); + opt->opts = opts; + QTAILQ_INSERT_TAIL(&opts->head, opt, next); + if (desc[i].name != NULL) { + opt->desc = desc+i; + } + if (value) { + opt->str = qemu_strdup(value); + } + if (qemu_opt_parse(opt) < 0) { + fprintf(stderr, "Failed to parse \"%s\" for \"%s.%s\"\n", opt->str, + opts->list->name, opt->name); + qemu_opt_del(opt); + return -1; + } + return 0; +} + +int qemu_opt_foreach(QemuOpts *opts, qemu_opt_loopfunc func, void *opaque, + int abort_on_failure) +{ + QemuOpt *opt; + int rc = 0; + + QTAILQ_FOREACH(opt, &opts->head, next) { + rc = func(opt->name, opt->str, opaque); + if (abort_on_failure && rc != 0) + break; + } + return rc; +} + +QemuOpts *qemu_opts_find(QemuOptsList *list, const char *id) +{ + QemuOpts *opts; + + QTAILQ_FOREACH(opts, &list->head, next) { + if (!opts->id) { + continue; + } + if (strcmp(opts->id, id) != 0) { + continue; + } + return opts; + } + return NULL; +} + +QemuOpts *qemu_opts_create(QemuOptsList *list, const char *id, int fail_if_exists) +{ + QemuOpts *opts = NULL; + + if (id) { + opts = qemu_opts_find(list, id); + if (opts != NULL) { + if (fail_if_exists) { + fprintf(stderr, "tried to create id \"%s\" twice for \"%s\"\n", + id, list->name); + return NULL; + } else { + return opts; + } + } + } + opts = qemu_mallocz(sizeof(*opts)); + if (id) { + opts->id = qemu_strdup(id); + } + opts->list = list; + QTAILQ_INIT(&opts->head); + QTAILQ_INSERT_TAIL(&list->head, opts, next); + return opts; +} + +int qemu_opts_set(QemuOptsList *list, const char *id, + const char *name, const char *value) +{ + QemuOpts *opts; + + opts = qemu_opts_create(list, id, 1); + if (opts == NULL) { + return -1; + } + return qemu_opt_set(opts, name, value); +} + +const char *qemu_opts_id(QemuOpts *opts) +{ + return opts->id; +} + +void qemu_opts_del(QemuOpts *opts) +{ + QemuOpt *opt; + + for (;;) { + opt = QTAILQ_FIRST(&opts->head); + if (opt == NULL) + break; + qemu_opt_del(opt); + } + QTAILQ_REMOVE(&opts->list->head, opts, next); + qemu_free(opts->id); + qemu_free(opts); +} + +int qemu_opts_print(QemuOpts *opts, void *dummy) +{ + QemuOpt *opt; + + fprintf(stderr, "%s: %s:", opts->list->name, + opts->id ? opts->id : ""); + QTAILQ_FOREACH(opt, &opts->head, next) { + fprintf(stderr, " %s=\"%s\"", opt->name, opt->str); + } + fprintf(stderr, "\n"); + return 0; +} + +int qemu_opts_do_parse(QemuOpts *opts, const char *params, const char *firstname) +{ + char option[128], value[1024]; + const char *p,*pe,*pc; + + for (p = params; *p != '\0'; p++) { + pe = strchr(p, '='); + pc = strchr(p, ','); + if (!pe || (pc && pc < pe)) { + /* found "foo,more" */ + if (p == params && firstname) { + /* implicitly named first option */ + pstrcpy(option, sizeof(option), firstname); + p = get_opt_value(value, sizeof(value), p); + } else { + /* option without value, probably a flag */ + p = get_opt_name(option, sizeof(option), p, ','); + if (strncmp(option, "no", 2) == 0) { + memmove(option, option+2, strlen(option+2)+1); + pstrcpy(value, sizeof(value), "off"); + } else { + pstrcpy(value, sizeof(value), "on"); + } + } + } else { + /* found "foo=bar,more" */ + p = get_opt_name(option, sizeof(option), p, '='); + if (*p != '=') { + break; + } + p++; + p = get_opt_value(value, sizeof(value), p); + } + if (strcmp(option, "id") != 0) { + /* store and parse */ + if (qemu_opt_set(opts, option, value) == -1) { + return -1; + } + } + if (*p != ',') { + break; + } + } + return 0; +} + +QemuOpts *qemu_opts_parse(QemuOptsList *list, const char *params, const char *firstname) +{ + char value[1024], *id = NULL; + const char *p; + QemuOpts *opts; + + if (strncmp(params, "id=", 3) == 0) { + get_opt_value(value, sizeof(value), params+3); + id = qemu_strdup(value); + } else if ((p = strstr(params, ",id=")) != NULL) { + get_opt_value(value, sizeof(value), p+4); + id = qemu_strdup(value); + } + opts = qemu_opts_create(list, id, 1); + if (opts == NULL) + return NULL; + + if (qemu_opts_do_parse(opts, params, firstname) != 0) { + qemu_opts_del(opts); + return NULL; + } + + return opts; +} + +/* Validate parsed opts against descriptions where no + * descriptions were provided in the QemuOptsList. + */ +int qemu_opts_validate(QemuOpts *opts, QemuOptDesc *desc) +{ + QemuOpt *opt; + + assert(opts->list->desc[0].name == NULL); + + QTAILQ_FOREACH(opt, &opts->head, next) { + int i; + + for (i = 0; desc[i].name != NULL; i++) { + if (strcmp(desc[i].name, opt->name) == 0) { + break; + } + } + if (desc[i].name == NULL) { + fprintf(stderr, "option \"%s\" is not valid for %s\n", + opt->name, opts->list->name); + return -1; + } + + opt->desc = &desc[i]; + + if (qemu_opt_parse(opt) < 0) { + return -1; + } + } + + return 0; +} + +int qemu_opts_foreach(QemuOptsList *list, qemu_opts_loopfunc func, void *opaque, + int abort_on_failure) +{ + QemuOpts *opts; + int rc = 0; + + QTAILQ_FOREACH(opts, &list->head, next) { + rc = func(opts, opaque); + if (abort_on_failure && rc != 0) + break; + } + return rc; +} -- cgit v1.1