qemu/qapi/string-input-visitor.c
<<
>>
Prefs
   1/*
   2 * String parsing visitor
   3 *
   4 * Copyright Red Hat, Inc. 2012-2016
   5 *
   6 * Author: Paolo Bonzini <pbonzini@redhat.com>
   7 *
   8 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
   9 * See the COPYING.LIB file in the top-level directory.
  10 *
  11 */
  12
  13#include "qemu/osdep.h"
  14#include "qapi/error.h"
  15#include "qemu-common.h"
  16#include "qapi/string-input-visitor.h"
  17#include "qapi/visitor-impl.h"
  18#include "qapi/qmp/qerror.h"
  19#include "qemu/option.h"
  20#include "qemu/queue.h"
  21#include "qemu/range.h"
  22
  23
  24struct StringInputVisitor
  25{
  26    Visitor visitor;
  27
  28    GList *ranges;
  29    GList *cur_range;
  30    int64_t cur;
  31
  32    const char *string;
  33    void *list; /* Only needed for sanity checking the caller */
  34};
  35
  36static StringInputVisitor *to_siv(Visitor *v)
  37{
  38    return container_of(v, StringInputVisitor, visitor);
  39}
  40
  41static void free_range(void *range, void *dummy)
  42{
  43    g_free(range);
  44}
  45
  46static int parse_str(StringInputVisitor *siv, const char *name, Error **errp)
  47{
  48    char *str = (char *) siv->string;
  49    long long start, end;
  50    Range *cur;
  51    char *endptr;
  52
  53    if (siv->ranges) {
  54        return 0;
  55    }
  56
  57    do {
  58        errno = 0;
  59        start = strtoll(str, &endptr, 0);
  60        if (errno == 0 && endptr > str) {
  61            if (*endptr == '\0') {
  62                cur = g_malloc0(sizeof(*cur));
  63                range_set_bounds(cur, start, start);
  64                siv->ranges = range_list_insert(siv->ranges, cur);
  65                cur = NULL;
  66                str = NULL;
  67            } else if (*endptr == '-') {
  68                str = endptr + 1;
  69                errno = 0;
  70                end = strtoll(str, &endptr, 0);
  71                if (errno == 0 && endptr > str && start <= end &&
  72                    (start > INT64_MAX - 65536 ||
  73                     end < start + 65536)) {
  74                    if (*endptr == '\0') {
  75                        cur = g_malloc0(sizeof(*cur));
  76                        range_set_bounds(cur, start, end);
  77                        siv->ranges = range_list_insert(siv->ranges, cur);
  78                        cur = NULL;
  79                        str = NULL;
  80                    } else if (*endptr == ',') {
  81                        str = endptr + 1;
  82                        cur = g_malloc0(sizeof(*cur));
  83                        range_set_bounds(cur, start, end);
  84                        siv->ranges = range_list_insert(siv->ranges, cur);
  85                        cur = NULL;
  86                    } else {
  87                        goto error;
  88                    }
  89                } else {
  90                    goto error;
  91                }
  92            } else if (*endptr == ',') {
  93                str = endptr + 1;
  94                cur = g_malloc0(sizeof(*cur));
  95                range_set_bounds(cur, start, start);
  96                siv->ranges = range_list_insert(siv->ranges, cur);
  97                cur = NULL;
  98            } else {
  99                goto error;
 100            }
 101        } else {
 102            goto error;
 103        }
 104    } while (str);
 105
 106    return 0;
 107error:
 108    g_list_foreach(siv->ranges, free_range, NULL);
 109    g_list_free(siv->ranges);
 110    siv->ranges = NULL;
 111    error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
 112               "an int64 value or range");
 113    return -1;
 114}
 115
 116static void
 117start_list(Visitor *v, const char *name, GenericList **list, size_t size,
 118           Error **errp)
 119{
 120    StringInputVisitor *siv = to_siv(v);
 121
 122    /* We don't support visits without a list */
 123    assert(list);
 124    siv->list = list;
 125
 126    if (parse_str(siv, name, errp) < 0) {
 127        *list = NULL;
 128        return;
 129    }
 130
 131    siv->cur_range = g_list_first(siv->ranges);
 132    if (siv->cur_range) {
 133        Range *r = siv->cur_range->data;
 134        if (r) {
 135            siv->cur = range_lob(r);
 136        }
 137        *list = g_malloc0(size);
 138    } else {
 139        *list = NULL;
 140    }
 141}
 142
 143static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
 144{
 145    StringInputVisitor *siv = to_siv(v);
 146    Range *r;
 147
 148    if (!siv->ranges || !siv->cur_range) {
 149        return NULL;
 150    }
 151
 152    r = siv->cur_range->data;
 153    if (!r) {
 154        return NULL;
 155    }
 156
 157    if (!range_contains(r, siv->cur)) {
 158        siv->cur_range = g_list_next(siv->cur_range);
 159        if (!siv->cur_range) {
 160            return NULL;
 161        }
 162        r = siv->cur_range->data;
 163        if (!r) {
 164            return NULL;
 165        }
 166        siv->cur = range_lob(r);
 167    }
 168
 169    tail->next = g_malloc0(size);
 170    return tail->next;
 171}
 172
 173static void end_list(Visitor *v, void **obj)
 174{
 175    StringInputVisitor *siv = to_siv(v);
 176
 177    assert(siv->list == obj);
 178}
 179
 180static void parse_type_int64(Visitor *v, const char *name, int64_t *obj,
 181                             Error **errp)
 182{
 183    StringInputVisitor *siv = to_siv(v);
 184
 185    if (!siv->string) {
 186        error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
 187                   "integer");
 188        return;
 189    }
 190
 191    if (parse_str(siv, name, errp) < 0) {
 192        return;
 193    }
 194
 195    if (!siv->ranges) {
 196        goto error;
 197    }
 198
 199    if (!siv->cur_range) {
 200        Range *r;
 201
 202        siv->cur_range = g_list_first(siv->ranges);
 203        if (!siv->cur_range) {
 204            goto error;
 205        }
 206
 207        r = siv->cur_range->data;
 208        if (!r) {
 209            goto error;
 210        }
 211
 212        siv->cur = range_lob(r);
 213    }
 214
 215    *obj = siv->cur;
 216    siv->cur++;
 217    return;
 218
 219error:
 220    error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
 221               "an int64 value or range");
 222}
 223
 224static void parse_type_uint64(Visitor *v, const char *name, uint64_t *obj,
 225                              Error **errp)
 226{
 227    /* FIXME: parse_type_int64 mishandles values over INT64_MAX */
 228    int64_t i;
 229    Error *err = NULL;
 230    parse_type_int64(v, name, &i, &err);
 231    if (err) {
 232        error_propagate(errp, err);
 233    } else {
 234        *obj = i;
 235    }
 236}
 237
 238static void parse_type_size(Visitor *v, const char *name, uint64_t *obj,
 239                            Error **errp)
 240{
 241    StringInputVisitor *siv = to_siv(v);
 242    Error *err = NULL;
 243    uint64_t val;
 244
 245    if (siv->string) {
 246        parse_option_size(name, siv->string, &val, &err);
 247    } else {
 248        error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
 249                   "size");
 250        return;
 251    }
 252    if (err) {
 253        error_propagate(errp, err);
 254        return;
 255    }
 256
 257    *obj = val;
 258}
 259
 260static void parse_type_bool(Visitor *v, const char *name, bool *obj,
 261                            Error **errp)
 262{
 263    StringInputVisitor *siv = to_siv(v);
 264
 265    if (siv->string) {
 266        if (!strcasecmp(siv->string, "on") ||
 267            !strcasecmp(siv->string, "yes") ||
 268            !strcasecmp(siv->string, "true")) {
 269            *obj = true;
 270            return;
 271        }
 272        if (!strcasecmp(siv->string, "off") ||
 273            !strcasecmp(siv->string, "no") ||
 274            !strcasecmp(siv->string, "false")) {
 275            *obj = false;
 276            return;
 277        }
 278    }
 279
 280    error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
 281               "boolean");
 282}
 283
 284static void parse_type_str(Visitor *v, const char *name, char **obj,
 285                           Error **errp)
 286{
 287    StringInputVisitor *siv = to_siv(v);
 288    if (siv->string) {
 289        *obj = g_strdup(siv->string);
 290    } else {
 291        *obj = NULL;
 292        error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
 293                   "string");
 294    }
 295}
 296
 297static void parse_type_number(Visitor *v, const char *name, double *obj,
 298                              Error **errp)
 299{
 300    StringInputVisitor *siv = to_siv(v);
 301    char *endp = (char *) siv->string;
 302    double val;
 303
 304    errno = 0;
 305    if (siv->string) {
 306        val = strtod(siv->string, &endp);
 307    }
 308    if (!siv->string || errno || endp == siv->string || *endp) {
 309        error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
 310                   "number");
 311        return;
 312    }
 313
 314    *obj = val;
 315}
 316
 317static void parse_optional(Visitor *v, const char *name, bool *present)
 318{
 319    StringInputVisitor *siv = to_siv(v);
 320
 321    if (!siv->string) {
 322        *present = false;
 323        return;
 324    }
 325
 326    *present = true;
 327}
 328
 329static void string_input_free(Visitor *v)
 330{
 331    StringInputVisitor *siv = to_siv(v);
 332
 333    g_list_foreach(siv->ranges, free_range, NULL);
 334    g_list_free(siv->ranges);
 335    g_free(siv);
 336}
 337
 338Visitor *string_input_visitor_new(const char *str)
 339{
 340    StringInputVisitor *v;
 341
 342    v = g_malloc0(sizeof(*v));
 343
 344    v->visitor.type = VISITOR_INPUT;
 345    v->visitor.type_int64 = parse_type_int64;
 346    v->visitor.type_uint64 = parse_type_uint64;
 347    v->visitor.type_size = parse_type_size;
 348    v->visitor.type_bool = parse_type_bool;
 349    v->visitor.type_str = parse_type_str;
 350    v->visitor.type_number = parse_type_number;
 351    v->visitor.start_list = start_list;
 352    v->visitor.next_list = next_list;
 353    v->visitor.end_list = end_list;
 354    v->visitor.optional = parse_optional;
 355    v->visitor.free = string_input_free;
 356
 357    v->string = str;
 358    return &v->visitor;
 359}
 360