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