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