qemu/qapi/string-input-visitor.c
<<
>>
Prefs
   1/*
   2 * String parsing visitor
   3 *
   4 * Copyright Red Hat, Inc. 2012
   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-common.h"
  14#include "qapi/string-input-visitor.h"
  15#include "qapi/visitor-impl.h"
  16#include "qapi/qmp/qerror.h"
  17#include "qemu/option.h"
  18#include "qemu/queue.h"
  19#include "qemu/range.h"
  20
  21
  22struct StringInputVisitor
  23{
  24    Visitor visitor;
  25
  26    bool head;
  27
  28    GList *ranges;
  29    GList *cur_range;
  30    int64_t cur;
  31
  32    const char *string;
  33};
  34
  35static void free_range(void *range, void *dummy)
  36{
  37    g_free(range);
  38}
  39
  40static void parse_str(StringInputVisitor *siv, Error **errp)
  41{
  42    char *str = (char *) siv->string;
  43    long long start, end;
  44    Range *cur;
  45    char *endptr;
  46
  47    if (siv->ranges) {
  48        return;
  49    }
  50
  51    do {
  52        errno = 0;
  53        start = strtoll(str, &endptr, 0);
  54        if (errno == 0 && endptr > str) {
  55            if (*endptr == '\0') {
  56                cur = g_malloc0(sizeof(*cur));
  57                cur->begin = start;
  58                cur->end = start + 1;
  59                siv->ranges = g_list_insert_sorted_merged(siv->ranges, cur,
  60                                                          range_compare);
  61                cur = NULL;
  62                str = NULL;
  63            } else if (*endptr == '-') {
  64                str = endptr + 1;
  65                errno = 0;
  66                end = strtoll(str, &endptr, 0);
  67                if (errno == 0 && endptr > str && start <= end &&
  68                    (start > INT64_MAX - 65536 ||
  69                     end < start + 65536)) {
  70                    if (*endptr == '\0') {
  71                        cur = g_malloc0(sizeof(*cur));
  72                        cur->begin = start;
  73                        cur->end = end + 1;
  74                        siv->ranges =
  75                            g_list_insert_sorted_merged(siv->ranges,
  76                                                        cur,
  77                                                        range_compare);
  78                        cur = NULL;
  79                        str = NULL;
  80                    } else if (*endptr == ',') {
  81                        str = endptr + 1;
  82                        cur = g_malloc0(sizeof(*cur));
  83                        cur->begin = start;
  84                        cur->end = end + 1;
  85                        siv->ranges =
  86                            g_list_insert_sorted_merged(siv->ranges,
  87                                                        cur,
  88                                                        range_compare);
  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                cur->begin = start;
 100                cur->end = start + 1;
 101                siv->ranges = g_list_insert_sorted_merged(siv->ranges,
 102                                                          cur,
 103                                                          range_compare);
 104                cur = NULL;
 105            } else {
 106                goto error;
 107            }
 108        } else {
 109            goto error;
 110        }
 111    } while (str);
 112
 113    return;
 114error:
 115    g_list_foreach(siv->ranges, free_range, NULL);
 116    g_list_free(siv->ranges);
 117    siv->ranges = NULL;
 118}
 119
 120static void
 121start_list(Visitor *v, const char *name, Error **errp)
 122{
 123    StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
 124
 125    parse_str(siv, errp);
 126
 127    siv->cur_range = g_list_first(siv->ranges);
 128    if (siv->cur_range) {
 129        Range *r = siv->cur_range->data;
 130        if (r) {
 131            siv->cur = r->begin;
 132        }
 133    }
 134}
 135
 136static GenericList *
 137next_list(Visitor *v, GenericList **list, Error **errp)
 138{
 139    StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
 140    GenericList **link;
 141    Range *r;
 142
 143    if (!siv->ranges || !siv->cur_range) {
 144        return NULL;
 145    }
 146
 147    r = siv->cur_range->data;
 148    if (!r) {
 149        return NULL;
 150    }
 151
 152    if (siv->cur < r->begin || siv->cur >= r->end) {
 153        siv->cur_range = g_list_next(siv->cur_range);
 154        if (!siv->cur_range) {
 155            return NULL;
 156        }
 157        r = siv->cur_range->data;
 158        if (!r) {
 159            return NULL;
 160        }
 161        siv->cur = r->begin;
 162    }
 163
 164    if (siv->head) {
 165        link = list;
 166        siv->head = false;
 167    } else {
 168        link = &(*list)->next;
 169    }
 170
 171    *link = g_malloc0(sizeof **link);
 172    return *link;
 173}
 174
 175static void
 176end_list(Visitor *v, Error **errp)
 177{
 178    StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
 179    siv->head = true;
 180}
 181
 182static void parse_type_int(Visitor *v, int64_t *obj, const char *name,
 183                           Error **errp)
 184{
 185    StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
 186
 187    if (!siv->string) {
 188        error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
 189                  "integer");
 190        return;
 191    }
 192
 193    parse_str(siv, errp);
 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 = r->begin;
 213    }
 214
 215    *obj = siv->cur;
 216    siv->cur++;
 217    return;
 218
 219error:
 220    error_set(errp, QERR_INVALID_PARAMETER_VALUE, name,
 221              "an int64 value or range");
 222}
 223
 224static void parse_type_size(Visitor *v, uint64_t *obj, const char *name,
 225                            Error **errp)
 226{
 227    StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
 228    Error *err = NULL;
 229    uint64_t val;
 230
 231    if (siv->string) {
 232        parse_option_size(name, siv->string, &val, &err);
 233    } else {
 234        error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
 235                  "size");
 236        return;
 237    }
 238    if (err) {
 239        error_propagate(errp, err);
 240        return;
 241    }
 242
 243    *obj = val;
 244}
 245
 246static void parse_type_bool(Visitor *v, bool *obj, const char *name,
 247                            Error **errp)
 248{
 249    StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
 250
 251    if (siv->string) {
 252        if (!strcasecmp(siv->string, "on") ||
 253            !strcasecmp(siv->string, "yes") ||
 254            !strcasecmp(siv->string, "true")) {
 255            *obj = true;
 256            return;
 257        }
 258        if (!strcasecmp(siv->string, "off") ||
 259            !strcasecmp(siv->string, "no") ||
 260            !strcasecmp(siv->string, "false")) {
 261            *obj = false;
 262            return;
 263        }
 264    }
 265
 266    error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
 267              "boolean");
 268}
 269
 270static void parse_type_str(Visitor *v, char **obj, const char *name,
 271                           Error **errp)
 272{
 273    StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
 274    if (siv->string) {
 275        *obj = g_strdup(siv->string);
 276    } else {
 277        error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
 278                  "string");
 279    }
 280}
 281
 282static void parse_type_number(Visitor *v, double *obj, const char *name,
 283                              Error **errp)
 284{
 285    StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
 286    char *endp = (char *) siv->string;
 287    double val;
 288
 289    errno = 0;
 290    if (siv->string) {
 291        val = strtod(siv->string, &endp);
 292    }
 293    if (!siv->string || errno || endp == siv->string || *endp) {
 294        error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
 295                  "number");
 296        return;
 297    }
 298
 299    *obj = val;
 300}
 301
 302static void parse_optional(Visitor *v, bool *present, const char *name,
 303                           Error **errp)
 304{
 305    StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
 306
 307    if (!siv->string) {
 308        *present = false;
 309        return;
 310    }
 311
 312    *present = true;
 313}
 314
 315Visitor *string_input_get_visitor(StringInputVisitor *v)
 316{
 317    return &v->visitor;
 318}
 319
 320void string_input_visitor_cleanup(StringInputVisitor *v)
 321{
 322    g_list_foreach(v->ranges, free_range, NULL);
 323    g_list_free(v->ranges);
 324    g_free(v);
 325}
 326
 327StringInputVisitor *string_input_visitor_new(const char *str)
 328{
 329    StringInputVisitor *v;
 330
 331    v = g_malloc0(sizeof(*v));
 332
 333    v->visitor.type_enum = input_type_enum;
 334    v->visitor.type_int = parse_type_int;
 335    v->visitor.type_size = parse_type_size;
 336    v->visitor.type_bool = parse_type_bool;
 337    v->visitor.type_str = parse_type_str;
 338    v->visitor.type_number = parse_type_number;
 339    v->visitor.start_list = start_list;
 340    v->visitor.next_list = next_list;
 341    v->visitor.end_list = end_list;
 342    v->visitor.optional = parse_optional;
 343
 344    v->string = str;
 345    v->head = true;
 346    return v;
 347}
 348