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