Add #defines needed by OpenSolaris, fix breakage by the #defines
[qemu] / qemu-option.c
index 8aac6ae..88298e4 100644 (file)
@@ -182,6 +182,59 @@ static int parse_option_bool(const char *name, const char *value, int *ret)
     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:
@@ -221,7 +274,7 @@ int set_option_parameter(QEMUOptionParameter *list, const char *name,
 
     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;
@@ -229,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 (-1 == parse_option_size(name, value, &list->value.n))
             return -1;
-        }
         break;
+
     default:
         fprintf(stderr, "Bug: Option '%s' has an unknown type\n", name);
         return -1;
@@ -305,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);
 }
 
 /*
@@ -434,3 +463,335 @@ void print_option_help(QEMUOptionParameter *list)
         list++;
     }
 }
+
+/* ------------------------------------------------------------------ */
+
+struct QemuOpt {
+    const char   *name;
+    const char   *str;
+
+    QemuOptDesc  *desc;
+    union {
+        int      bool;
+        uint64_t uint;
+    } value;
+
+    QemuOpts     *opts;
+    QTAILQ_ENTRY(QemuOpt) next;
+};
+
+struct QemuOpts {
+    const char *id;
+    QemuOptsList *list;
+    QTAILQ_HEAD(, QemuOpt) head;
+    QTAILQ_ENTRY(QemuOpts) next;
+};
+
+static QemuOpt *qemu_opt_find(QemuOpts *opts, const char *name)
+{
+    QemuOpt *opt;
+
+    QTAILQ_FOREACH(opt, &opts->head, 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.bool;
+}
+
+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.bool);
+    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;
+
+    opt = qemu_opt_find(opts, name);
+    if (!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;
+        }
+    }
+    qemu_free((/* !const */ char*)opt->str);
+    opt->str = NULL;
+    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) {
+        fprintf(stderr, "id \"%s\" not found for \"%s\"\n",
+                id, list->name);
+        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);
+}
+
+int qemu_opts_print(QemuOpts *opts, void *dummy)
+{
+    QemuOpt *opt;
+
+    fprintf(stderr, "%s: %s:", opts->list->name,
+            opts->id ? opts->id : "<noid>");
+    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[128];
+    const char *p,*pe,*pc;
+
+    p = params;
+    for(;;) {
+        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 (-1 == qemu_opt_set(opts, option, value)) {
+                return -1;
+            }
+        }
+        if (*p != ',') {
+            break;
+        }
+        p++;
+    }
+    return 0;
+}
+
+QemuOpts *qemu_opts_parse(QemuOptsList *list, const char *params, const char *firstname)
+{
+    char value[128], *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;
+}
+
+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;
+}