string-input-visitor.c 8.9 KB
Newer Older
P
Paolo Bonzini 已提交
1 2 3
/*
 * String parsing visitor
 *
4
 * Copyright Red Hat, Inc. 2012-2016
P
Paolo Bonzini 已提交
5 6 7 8 9 10 11 12
 *
 * Author: Paolo Bonzini <pbonzini@redhat.com>
 *
 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
 * See the COPYING.LIB file in the top-level directory.
 *
 */

P
Peter Maydell 已提交
13
#include "qemu/osdep.h"
14
#include "qapi/error.h"
P
Paolo Bonzini 已提交
15
#include "qemu-common.h"
16 17 18
#include "qapi/string-input-visitor.h"
#include "qapi/visitor-impl.h"
#include "qapi/qmp/qerror.h"
M
Max Reitz 已提交
19
#include "qapi/qmp/qnull.h"
20
#include "qemu/option.h"
21 22
#include "qemu/queue.h"
#include "qemu/range.h"
23
#include "qemu/cutils.h"
24

P
Paolo Bonzini 已提交
25 26 27 28

struct StringInputVisitor
{
    Visitor visitor;
29 30 31 32 33

    GList *ranges;
    GList *cur_range;
    int64_t cur;

P
Paolo Bonzini 已提交
34
    const char *string;
E
Eric Blake 已提交
35
    void *list; /* Only needed for sanity checking the caller */
P
Paolo Bonzini 已提交
36 37
};

38 39 40 41 42
static StringInputVisitor *to_siv(Visitor *v)
{
    return container_of(v, StringInputVisitor, visitor);
}

43 44 45 46 47
static void free_range(void *range, void *dummy)
{
    g_free(range);
}

48
static int parse_str(StringInputVisitor *siv, const char *name, Error **errp)
49 50 51 52 53 54 55
{
    char *str = (char *) siv->string;
    long long start, end;
    Range *cur;
    char *endptr;

    if (siv->ranges) {
56
        return 0;
57 58
    }

59 60 61 62
    if (!*str) {
        return 0;
    }

63
    do {
64
        errno = 0;
65
        start = strtoll(str, &endptr, 0);
66
        if (errno == 0 && endptr > str) {
67 68
            if (*endptr == '\0') {
                cur = g_malloc0(sizeof(*cur));
69
                range_set_bounds(cur, start, start);
E
Eric Blake 已提交
70
                siv->ranges = range_list_insert(siv->ranges, cur);
71 72 73 74
                cur = NULL;
                str = NULL;
            } else if (*endptr == '-') {
                str = endptr + 1;
75
                errno = 0;
76
                end = strtoll(str, &endptr, 0);
77
                if (errno == 0 && endptr > str && start <= end &&
78 79 80 81
                    (start > INT64_MAX - 65536 ||
                     end < start + 65536)) {
                    if (*endptr == '\0') {
                        cur = g_malloc0(sizeof(*cur));
82
                        range_set_bounds(cur, start, end);
E
Eric Blake 已提交
83
                        siv->ranges = range_list_insert(siv->ranges, cur);
84 85 86 87 88
                        cur = NULL;
                        str = NULL;
                    } else if (*endptr == ',') {
                        str = endptr + 1;
                        cur = g_malloc0(sizeof(*cur));
89
                        range_set_bounds(cur, start, end);
E
Eric Blake 已提交
90
                        siv->ranges = range_list_insert(siv->ranges, cur);
91 92 93 94 95 96 97 98 99 100
                        cur = NULL;
                    } else {
                        goto error;
                    }
                } else {
                    goto error;
                }
            } else if (*endptr == ',') {
                str = endptr + 1;
                cur = g_malloc0(sizeof(*cur));
101
                range_set_bounds(cur, start, start);
E
Eric Blake 已提交
102
                siv->ranges = range_list_insert(siv->ranges, cur);
103 104 105 106 107 108 109 110 111
                cur = NULL;
            } else {
                goto error;
            }
        } else {
            goto error;
        }
    } while (str);

112
    return 0;
113
error:
114 115 116
    g_list_foreach(siv->ranges, free_range, NULL);
    g_list_free(siv->ranges);
    siv->ranges = NULL;
117 118 119
    error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
               "an int64 value or range");
    return -1;
120 121 122
}

static void
123 124
start_list(Visitor *v, const char *name, GenericList **list, size_t size,
           Error **errp)
125
{
126
    StringInputVisitor *siv = to_siv(v);
127

128 129
    /* We don't support visits without a list */
    assert(list);
E
Eric Blake 已提交
130
    siv->list = list;
131

132
    if (parse_str(siv, name, errp) < 0) {
133
        *list = NULL;
134 135
        return;
    }
136 137 138 139 140

    siv->cur_range = g_list_first(siv->ranges);
    if (siv->cur_range) {
        Range *r = siv->cur_range->data;
        if (r) {
141
            siv->cur = range_lob(r);
142
        }
143 144 145
        *list = g_malloc0(size);
    } else {
        *list = NULL;
146 147 148
    }
}

149
static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
150
{
151
    StringInputVisitor *siv = to_siv(v);
152 153 154 155 156 157 158 159 160 161 162
    Range *r;

    if (!siv->ranges || !siv->cur_range) {
        return NULL;
    }

    r = siv->cur_range->data;
    if (!r) {
        return NULL;
    }

163
    if (!range_contains(r, siv->cur)) {
164 165 166 167 168 169 170 171
        siv->cur_range = g_list_next(siv->cur_range);
        if (!siv->cur_range) {
            return NULL;
        }
        r = siv->cur_range->data;
        if (!r) {
            return NULL;
        }
172
        siv->cur = range_lob(r);
173 174
    }

175 176
    tail->next = g_malloc0(size);
    return tail->next;
177 178
}

179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
static void check_list(Visitor *v, Error **errp)
{
    const StringInputVisitor *siv = to_siv(v);
    Range *r;
    GList *cur_range;

    if (!siv->ranges || !siv->cur_range) {
        return;
    }

    r = siv->cur_range->data;
    if (!r) {
        return;
    }

    if (!range_contains(r, siv->cur)) {
        cur_range = g_list_next(siv->cur_range);
        if (!cur_range) {
            return;
        }
        r = cur_range->data;
        if (!r) {
            return;
        }
    }

    error_setg(errp, "Range contains too many values");
}

E
Eric Blake 已提交
208
static void end_list(Visitor *v, void **obj)
209
{
E
Eric Blake 已提交
210 211 212
    StringInputVisitor *siv = to_siv(v);

    assert(siv->list == obj);
213 214
}

215
static void parse_type_int64(Visitor *v, const char *name, int64_t *obj,
216
                             Error **errp)
P
Paolo Bonzini 已提交
217
{
218
    StringInputVisitor *siv = to_siv(v);
P
Paolo Bonzini 已提交
219

220 221 222
    if (parse_str(siv, name, errp) < 0) {
        return;
    }
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240

    if (!siv->ranges) {
        goto error;
    }

    if (!siv->cur_range) {
        Range *r;

        siv->cur_range = g_list_first(siv->ranges);
        if (!siv->cur_range) {
            goto error;
        }

        r = siv->cur_range->data;
        if (!r) {
            goto error;
        }

241
        siv->cur = range_lob(r);
242 243 244 245 246 247 248
    }

    *obj = siv->cur;
    siv->cur++;
    return;

error:
249
    error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
250
               "an int64 value or range");
P
Paolo Bonzini 已提交
251 252
}

253
static void parse_type_uint64(Visitor *v, const char *name, uint64_t *obj,
254 255 256 257 258
                              Error **errp)
{
    /* FIXME: parse_type_int64 mishandles values over INT64_MAX */
    int64_t i;
    Error *err = NULL;
259
    parse_type_int64(v, name, &i, &err);
260 261 262 263 264 265 266
    if (err) {
        error_propagate(errp, err);
    } else {
        *obj = i;
    }
}

267
static void parse_type_size(Visitor *v, const char *name, uint64_t *obj,
268 269
                            Error **errp)
{
270
    StringInputVisitor *siv = to_siv(v);
271 272 273
    Error *err = NULL;
    uint64_t val;

274
    parse_option_size(name, siv->string, &val, &err);
275 276 277 278 279 280 281 282
    if (err) {
        error_propagate(errp, err);
        return;
    }

    *obj = val;
}

283
static void parse_type_bool(Visitor *v, const char *name, bool *obj,
P
Paolo Bonzini 已提交
284 285
                            Error **errp)
{
286
    StringInputVisitor *siv = to_siv(v);
P
Paolo Bonzini 已提交
287

288 289 290 291 292 293 294 295 296 297 298
    if (!strcasecmp(siv->string, "on") ||
        !strcasecmp(siv->string, "yes") ||
        !strcasecmp(siv->string, "true")) {
        *obj = true;
        return;
    }
    if (!strcasecmp(siv->string, "off") ||
        !strcasecmp(siv->string, "no") ||
        !strcasecmp(siv->string, "false")) {
        *obj = false;
        return;
P
Paolo Bonzini 已提交
299 300
    }

301 302
    error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
               "boolean");
P
Paolo Bonzini 已提交
303 304
}

305
static void parse_type_str(Visitor *v, const char *name, char **obj,
P
Paolo Bonzini 已提交
306 307
                           Error **errp)
{
308
    StringInputVisitor *siv = to_siv(v);
309 310

    *obj = g_strdup(siv->string);
P
Paolo Bonzini 已提交
311 312
}

313
static void parse_type_number(Visitor *v, const char *name, double *obj,
P
Paolo Bonzini 已提交
314 315
                              Error **errp)
{
316
    StringInputVisitor *siv = to_siv(v);
P
Paolo Bonzini 已提交
317 318
    double val;

319
    if (qemu_strtod_finite(siv->string, NULL, &val)) {
320 321
        error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
                   "number");
P
Paolo Bonzini 已提交
322 323 324 325 326 327
        return;
    }

    *obj = val;
}

328 329
static void parse_type_null(Visitor *v, const char *name, QNull **obj,
                            Error **errp)
330 331 332
{
    StringInputVisitor *siv = to_siv(v);

333 334
    *obj = NULL;

335 336 337
    if (!siv->string || siv->string[0]) {
        error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
                   "null");
338
        return;
339
    }
340 341

    *obj = qnull();
342 343
}

E
Eric Blake 已提交
344 345 346 347
static void string_input_free(Visitor *v)
{
    StringInputVisitor *siv = to_siv(v);

348 349 350
    g_list_foreach(siv->ranges, free_range, NULL);
    g_list_free(siv->ranges);
    g_free(siv);
P
Paolo Bonzini 已提交
351 352
}

353
Visitor *string_input_visitor_new(const char *str)
P
Paolo Bonzini 已提交
354 355 356
{
    StringInputVisitor *v;

357
    assert(str);
P
Paolo Bonzini 已提交
358 359
    v = g_malloc0(sizeof(*v));

360
    v->visitor.type = VISITOR_INPUT;
361
    v->visitor.type_int64 = parse_type_int64;
362
    v->visitor.type_uint64 = parse_type_uint64;
363
    v->visitor.type_size = parse_type_size;
P
Paolo Bonzini 已提交
364 365 366
    v->visitor.type_bool = parse_type_bool;
    v->visitor.type_str = parse_type_str;
    v->visitor.type_number = parse_type_number;
367
    v->visitor.type_null = parse_type_null;
368 369
    v->visitor.start_list = start_list;
    v->visitor.next_list = next_list;
370
    v->visitor.check_list = check_list;
371
    v->visitor.end_list = end_list;
E
Eric Blake 已提交
372
    v->visitor.free = string_input_free;
P
Paolo Bonzini 已提交
373 374

    v->string = str;
375
    return &v->visitor;
P
Paolo Bonzini 已提交
376
}