2 * String parsing visitor
4 * Copyright Red Hat, Inc. 2012-2016
6 * Author: Paolo Bonzini <pbonzini@redhat.com>
7 * David Hildenbrand <david@redhat.com>
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
;