2 * String parsing visitor
4 * Copyright Red Hat, Inc. 2012-2016
9 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
10 * See the COPYING.LIB file in the top-level directory.
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/cutils.h"
23 typedef enum ListMode {
24 /* no list parsing active / no list expected */
26 /* we have an unparsed string remaining */
28 /* we have an unfinished int64 range */
30 /* we have an unfinished uint64 range */
32 /* we have parsed the string completely and no range is remaining */
36 /* protect against DOS attacks, limit the amount of elements per range */
37 #define RANGE_MAX_ELEMENTS 65536
39 typedef union RangeElement {
44 struct StringInputVisitor
48 /* List parsing state */
50 RangeElement rangeNext;
51 RangeElement rangeEnd;
52 const char *unparsed_string;
55 /* The original string to parse */
59 static StringInputVisitor *to_siv(Visitor *v)
61 return container_of(v, StringInputVisitor, visitor);
64 static void start_list(Visitor *v, const char *name, GenericList **list,
65 size_t size, Error **errp)
67 StringInputVisitor *siv = to_siv(v);
69 assert(siv->lm == LM_NONE);
71 siv->unparsed_string = siv->string;
73 if (!siv->string[0]) {
80 *list = g_malloc0(size);
82 siv->lm = LM_UNPARSED;
86 static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
88 StringInputVisitor *siv = to_siv(v);
96 /* we have an unparsed string or something left in a range */
102 tail->next = g_malloc0(size);
106 static void check_list(Visitor *v, Error **errp)
108 const StringInputVisitor *siv = to_siv(v);
112 case LM_UINT64_RANGE:
114 error_setg(errp, "Fewer list elements expected");
123 static void end_list(Visitor *v, void **obj)
125 StringInputVisitor *siv = to_siv(v);
127 assert(siv->lm != LM_NONE);
128 assert(siv->list == obj);
130 siv->unparsed_string = NULL;
134 static int try_parse_int64_list_entry(StringInputVisitor *siv, int64_t *obj)
139 /* parse a simple int64 or range */
140 if (qemu_strtoi64(siv->unparsed_string, &endptr, 0, &start)) {
147 siv->unparsed_string = endptr;
150 siv->unparsed_string = endptr + 1;
153 /* parse the end of the range */
154 if (qemu_strtoi64(endptr + 1, &endptr, 0, &end)) {
157 if (start > end || end - start >= RANGE_MAX_ELEMENTS) {
162 siv->unparsed_string = endptr;
165 siv->unparsed_string = endptr + 1;
175 /* we have a proper range (with maybe only one element) */
176 siv->lm = LM_INT64_RANGE;
177 siv->rangeNext.i64 = start;
178 siv->rangeEnd.i64 = end;
182 static void parse_type_int64(Visitor *v, const char *name, int64_t *obj,
185 StringInputVisitor *siv = to_siv(v);
190 /* just parse a simple int64, bail out if not completely consumed */
191 if (qemu_strtoi64(siv->string, NULL, 0, &val)) {
192 error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
193 name ? name : "null", "int64");
199 if (try_parse_int64_list_entry(siv, obj)) {
200 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
201 "list of int64 values or ranges");
204 assert(siv->lm == LM_INT64_RANGE);
207 /* return the next element in the range */
208 assert(siv->rangeNext.i64 <= siv->rangeEnd.i64);
209 *obj = siv->rangeNext.i64++;
211 if (siv->rangeNext.i64 > siv->rangeEnd.i64 || *obj == INT64_MAX) {
212 /* end of range, check if there is more to parse */
213 siv->lm = siv->unparsed_string[0] ? LM_UNPARSED : LM_END;
217 error_setg(errp, "Fewer list elements expected");
224 static int try_parse_uint64_list_entry(StringInputVisitor *siv, uint64_t *obj)
229 /* parse a simple uint64 or range */
230 if (qemu_strtou64(siv->unparsed_string, &endptr, 0, &start)) {
237 siv->unparsed_string = endptr;
240 siv->unparsed_string = endptr + 1;
243 /* parse the end of the range */
244 if (qemu_strtou64(endptr + 1, &endptr, 0, &end)) {
247 if (start > end || end - start >= RANGE_MAX_ELEMENTS) {
252 siv->unparsed_string = endptr;
255 siv->unparsed_string = endptr + 1;
265 /* we have a proper range (with maybe only one element) */
266 siv->lm = LM_UINT64_RANGE;
267 siv->rangeNext.u64 = start;
268 siv->rangeEnd.u64 = end;
272 static void parse_type_uint64(Visitor *v, const char *name, uint64_t *obj,
275 StringInputVisitor *siv = to_siv(v);
280 /* just parse a simple uint64, bail out if not completely consumed */
281 if (qemu_strtou64(siv->string, NULL, 0, &val)) {
282 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
289 if (try_parse_uint64_list_entry(siv, obj)) {
290 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
291 "list of uint64 values or ranges");
294 assert(siv->lm == LM_UINT64_RANGE);
296 case LM_UINT64_RANGE:
297 /* return the next element in the range */
298 assert(siv->rangeNext.u64 <= siv->rangeEnd.u64);
299 *obj = siv->rangeNext.u64++;
301 if (siv->rangeNext.u64 > siv->rangeEnd.u64 || *obj == UINT64_MAX) {
302 /* end of range, check if there is more to parse */
303 siv->lm = siv->unparsed_string[0] ? LM_UNPARSED : LM_END;
307 error_setg(errp, "Fewer list elements expected");
314 static void parse_type_size(Visitor *v, const char *name, uint64_t *obj,
317 StringInputVisitor *siv = to_siv(v);
321 assert(siv->lm == LM_NONE);
322 parse_option_size(name, siv->string, &val, &err);
324 error_propagate(errp, err);
331 static void parse_type_bool(Visitor *v, const char *name, bool *obj,
334 StringInputVisitor *siv = to_siv(v);
336 assert(siv->lm == LM_NONE);
337 if (!strcasecmp(siv->string, "on") ||
338 !strcasecmp(siv->string, "yes") ||
339 !strcasecmp(siv->string, "true")) {
343 if (!strcasecmp(siv->string, "off") ||
344 !strcasecmp(siv->string, "no") ||
345 !strcasecmp(siv->string, "false")) {
350 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
354 static void parse_type_str(Visitor *v, const char *name, char **obj,
357 StringInputVisitor *siv = to_siv(v);
359 assert(siv->lm == LM_NONE);
360 *obj = g_strdup(siv->string);
363 static void parse_type_number(Visitor *v, const char *name, double *obj,
366 StringInputVisitor *siv = to_siv(v);
369 assert(siv->lm == LM_NONE);
370 if (qemu_strtod_finite(siv->string, NULL, &val)) {
371 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
379 static void parse_type_null(Visitor *v, const char *name, QNull **obj,
382 StringInputVisitor *siv = to_siv(v);
384 assert(siv->lm == LM_NONE);
387 if (siv->string[0]) {
388 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
396 static void string_input_free(Visitor *v)
398 StringInputVisitor *siv = to_siv(v);
403 Visitor *string_input_visitor_new(const char *str)
405 StringInputVisitor *v;
408 v = g_malloc0(sizeof(*v));
410 v->visitor.type = VISITOR_INPUT;
411 v->visitor.type_int64 = parse_type_int64;
412 v->visitor.type_uint64 = parse_type_uint64;
413 v->visitor.type_size = parse_type_size;
414 v->visitor.type_bool = parse_type_bool;
415 v->visitor.type_str = parse_type_str;
416 v->visitor.type_number = parse_type_number;
417 v->visitor.type_null = parse_type_null;
418 v->visitor.start_list = start_list;
419 v->visitor.next_list = next_list;
420 v->visitor.check_list = check_list;
421 v->visitor.end_list = end_list;
422 v->visitor.free = string_input_free;