]> git.proxmox.com Git - mirror_qemu.git/blame - qapi/string-input-visitor.c
qapi,risc-v: add query-cpu-model-expansion
[mirror_qemu.git] / qapi / string-input-visitor.c
CommitLineData
a020f980
PB
1/*
2 * String parsing visitor
3 *
08f9541d 4 * Copyright Red Hat, Inc. 2012-2016
a020f980
PB
5 *
6 * Author: Paolo Bonzini <pbonzini@redhat.com>
c9fba9de 7 * David Hildenbrand <david@redhat.com>
a020f980
PB
8 *
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.
a020f980
PB
11 */
12
cbf21151 13#include "qemu/osdep.h"
da34e65c 14#include "qapi/error.h"
7b1b5d19
PB
15#include "qapi/string-input-visitor.h"
16#include "qapi/visitor-impl.h"
17#include "qapi/qmp/qerror.h"
84be629d 18#include "qapi/qmp/qnull.h"
a5829ccf 19#include "qemu/option.h"
4b69d4c3 20#include "qemu/cutils.h"
659268ff 21
c9fba9de
DH
22typedef enum ListMode {
23 /* no list parsing active / no list expected */
24 LM_NONE,
25 /* we have an unparsed string remaining */
26 LM_UNPARSED,
27 /* we have an unfinished int64 range */
28 LM_INT64_RANGE,
29 /* we have an unfinished uint64 range */
30 LM_UINT64_RANGE,
31 /* we have parsed the string completely and no range is remaining */
32 LM_END,
33} ListMode;
34
35/* protect against DOS attacks, limit the amount of elements per range */
36#define RANGE_MAX_ELEMENTS 65536
37
38typedef union RangeElement {
39 int64_t i64;
40 uint64_t u64;
41} RangeElement;
a020f980
PB
42
43struct StringInputVisitor
44{
45 Visitor visitor;
659268ff 46
c9fba9de
DH
47 /* List parsing state */
48 ListMode lm;
49 RangeElement rangeNext;
50 RangeElement rangeEnd;
51 const char *unparsed_string;
52 void *list;
659268ff 53
c9fba9de 54 /* The original string to parse */
a020f980
PB
55 const char *string;
56};
57
d7bea75d
EB
58static StringInputVisitor *to_siv(Visitor *v)
59{
60 return container_of(v, StringInputVisitor, visitor);
61}
62
012d4c96 63static bool start_list(Visitor *v, const char *name, GenericList **list,
c9fba9de 64 size_t size, Error **errp)
659268ff 65{
d7bea75d 66 StringInputVisitor *siv = to_siv(v);
659268ff 67
c9fba9de 68 assert(siv->lm == LM_NONE);
1158bb2a 69 siv->list = list;
c9fba9de 70 siv->unparsed_string = siv->string;
d9f62dde 71
c9fba9de
DH
72 if (!siv->string[0]) {
73 if (list) {
74 *list = NULL;
659268ff 75 }
c9fba9de 76 siv->lm = LM_END;
d9f62dde 77 } else {
c9fba9de
DH
78 if (list) {
79 *list = g_malloc0(size);
80 }
81 siv->lm = LM_UNPARSED;
659268ff 82 }
012d4c96 83 return true;
659268ff
HT
84}
85
d9f62dde 86static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
659268ff 87{
d7bea75d 88 StringInputVisitor *siv = to_siv(v);
659268ff 89
c9fba9de
DH
90 switch (siv->lm) {
91 case LM_END:
659268ff 92 return NULL;
c9fba9de
DH
93 case LM_INT64_RANGE:
94 case LM_UINT64_RANGE:
95 case LM_UNPARSED:
96 /* we have an unparsed string or something left in a range */
97 break;
98 default:
99 abort();
659268ff
HT
100 }
101
d9f62dde
EB
102 tail->next = g_malloc0(size);
103 return tail->next;
659268ff
HT
104}
105
012d4c96 106static bool check_list(Visitor *v, Error **errp)
a4a1c70d
MA
107{
108 const StringInputVisitor *siv = to_siv(v);
a4a1c70d 109
c9fba9de
DH
110 switch (siv->lm) {
111 case LM_INT64_RANGE:
112 case LM_UINT64_RANGE:
113 case LM_UNPARSED:
114 error_setg(errp, "Fewer list elements expected");
012d4c96 115 return false;
c9fba9de 116 case LM_END:
012d4c96 117 return true;
c9fba9de
DH
118 default:
119 abort();
a4a1c70d 120 }
a4a1c70d
MA
121}
122
1158bb2a 123static void end_list(Visitor *v, void **obj)
659268ff 124{
1158bb2a
EB
125 StringInputVisitor *siv = to_siv(v);
126
c9fba9de 127 assert(siv->lm != LM_NONE);
1158bb2a 128 assert(siv->list == obj);
c9fba9de
DH
129 siv->list = NULL;
130 siv->unparsed_string = NULL;
131 siv->lm = LM_NONE;
132}
133
134static int try_parse_int64_list_entry(StringInputVisitor *siv, int64_t *obj)
135{
136 const char *endptr;
137 int64_t start, end;
138
139 /* parse a simple int64 or range */
140 if (qemu_strtoi64(siv->unparsed_string, &endptr, 0, &start)) {
141 return -EINVAL;
142 }
143 end = start;
144
145 switch (endptr[0]) {
146 case '\0':
147 siv->unparsed_string = endptr;
148 break;
149 case ',':
150 siv->unparsed_string = endptr + 1;
151 break;
152 case '-':
153 /* parse the end of the range */
154 if (qemu_strtoi64(endptr + 1, &endptr, 0, &end)) {
155 return -EINVAL;
156 }
157 if (start > end || end - start >= RANGE_MAX_ELEMENTS) {
158 return -EINVAL;
159 }
160 switch (endptr[0]) {
161 case '\0':
162 siv->unparsed_string = endptr;
163 break;
164 case ',':
165 siv->unparsed_string = endptr + 1;
166 break;
167 default:
168 return -EINVAL;
169 }
170 break;
171 default:
172 return -EINVAL;
173 }
174
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;
179 return 0;
659268ff
HT
180}
181
012d4c96 182static bool parse_type_int64(Visitor *v, const char *name, int64_t *obj,
4c40314a 183 Error **errp)
a020f980 184{
d7bea75d 185 StringInputVisitor *siv = to_siv(v);
c9fba9de
DH
186 int64_t val;
187
188 switch (siv->lm) {
189 case LM_NONE:
190 /* just parse a simple int64, bail out if not completely consumed */
191 if (qemu_strtoi64(siv->string, NULL, 0, &val)) {
012d4c96
MA
192 error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
193 name ? name : "null", "int64");
194 return false;
c9fba9de
DH
195 }
196 *obj = val;
012d4c96 197 return true;
c9fba9de
DH
198 case LM_UNPARSED:
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");
012d4c96 202 return false;
c9fba9de
DH
203 }
204 assert(siv->lm == LM_INT64_RANGE);
205 /* fall through */
206 case LM_INT64_RANGE:
207 /* return the next element in the range */
208 assert(siv->rangeNext.i64 <= siv->rangeEnd.i64);
209 *obj = siv->rangeNext.i64++;
210
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;
214 }
012d4c96 215 return true;
c9fba9de
DH
216 case LM_END:
217 error_setg(errp, "Fewer list elements expected");
012d4c96 218 return false;
c9fba9de
DH
219 default:
220 abort();
74f24cb6 221 }
c9fba9de 222}
659268ff 223
c9fba9de
DH
224static int try_parse_uint64_list_entry(StringInputVisitor *siv, uint64_t *obj)
225{
226 const char *endptr;
227 uint64_t start, end;
659268ff 228
c9fba9de
DH
229 /* parse a simple uint64 or range */
230 if (qemu_strtou64(siv->unparsed_string, &endptr, 0, &start)) {
231 return -EINVAL;
232 }
233 end = start;
234
235 switch (endptr[0]) {
236 case '\0':
237 siv->unparsed_string = endptr;
238 break;
239 case ',':
240 siv->unparsed_string = endptr + 1;
241 break;
242 case '-':
243 /* parse the end of the range */
244 if (qemu_strtou64(endptr + 1, &endptr, 0, &end)) {
245 return -EINVAL;
659268ff 246 }
c9fba9de
DH
247 if (start > end || end - start >= RANGE_MAX_ELEMENTS) {
248 return -EINVAL;
659268ff 249 }
c9fba9de
DH
250 switch (endptr[0]) {
251 case '\0':
252 siv->unparsed_string = endptr;
253 break;
254 case ',':
255 siv->unparsed_string = endptr + 1;
256 break;
257 default:
258 return -EINVAL;
259 }
260 break;
261 default:
262 return -EINVAL;
659268ff
HT
263 }
264
c9fba9de
DH
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;
269 return 0;
a020f980
PB
270}
271
012d4c96 272static bool parse_type_uint64(Visitor *v, const char *name, uint64_t *obj,
f755dea7
EB
273 Error **errp)
274{
c9fba9de
DH
275 StringInputVisitor *siv = to_siv(v);
276 uint64_t val;
277
278 switch (siv->lm) {
279 case LM_NONE:
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",
283 "uint64");
012d4c96 284 return false;
c9fba9de
DH
285 }
286 *obj = val;
012d4c96 287 return true;
c9fba9de
DH
288 case LM_UNPARSED:
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");
012d4c96 292 return false;
c9fba9de
DH
293 }
294 assert(siv->lm == LM_UINT64_RANGE);
295 /* fall through */
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++;
300
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;
304 }
012d4c96 305 return true;
c9fba9de
DH
306 case LM_END:
307 error_setg(errp, "Fewer list elements expected");
012d4c96 308 return false;
c9fba9de
DH
309 default:
310 abort();
f755dea7
EB
311 }
312}
313
012d4c96 314static bool parse_type_size(Visitor *v, const char *name, uint64_t *obj,
a5829ccf
PB
315 Error **errp)
316{
d7bea75d 317 StringInputVisitor *siv = to_siv(v);
a5829ccf
PB
318 uint64_t val;
319
c9fba9de 320 assert(siv->lm == LM_NONE);
668f62ec 321 if (!parse_option_size(name, siv->string, &val, errp)) {
012d4c96 322 return false;
a5829ccf
PB
323 }
324
325 *obj = val;
012d4c96 326 return true;
a5829ccf
PB
327}
328
012d4c96 329static bool parse_type_bool(Visitor *v, const char *name, bool *obj,
a020f980
PB
330 Error **errp)
331{
d7bea75d 332 StringInputVisitor *siv = to_siv(v);
a020f980 333
c9fba9de 334 assert(siv->lm == LM_NONE);
372bcb25 335 return qapi_bool_parse(name ? name : "null", siv->string, obj, errp);
a020f980
PB
336}
337
012d4c96 338static bool parse_type_str(Visitor *v, const char *name, char **obj,
a020f980
PB
339 Error **errp)
340{
d7bea75d 341 StringInputVisitor *siv = to_siv(v);
f332e830 342
c9fba9de 343 assert(siv->lm == LM_NONE);
f332e830 344 *obj = g_strdup(siv->string);
012d4c96 345 return true;
a020f980
PB
346}
347
012d4c96 348static bool parse_type_number(Visitor *v, const char *name, double *obj,
a020f980
PB
349 Error **errp)
350{
d7bea75d 351 StringInputVisitor *siv = to_siv(v);
a020f980
PB
352 double val;
353
c9fba9de 354 assert(siv->lm == LM_NONE);
4b69d4c3 355 if (qemu_strtod_finite(siv->string, NULL, &val)) {
c6bd8c70
MA
356 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
357 "number");
012d4c96 358 return false;
a020f980
PB
359 }
360
361 *obj = val;
012d4c96 362 return true;
a020f980
PB
363}
364
012d4c96 365static bool parse_type_null(Visitor *v, const char *name, QNull **obj,
d2f95f4d 366 Error **errp)
a7333712
GK
367{
368 StringInputVisitor *siv = to_siv(v);
369
c9fba9de 370 assert(siv->lm == LM_NONE);
d2f95f4d
MA
371 *obj = NULL;
372
c9fba9de 373 if (siv->string[0]) {
a7333712
GK
374 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
375 "null");
012d4c96 376 return false;
a7333712 377 }
d2f95f4d
MA
378
379 *obj = qnull();
012d4c96 380 return true;
a7333712
GK
381}
382
2c0ef9f4
EB
383static void string_input_free(Visitor *v)
384{
385 StringInputVisitor *siv = to_siv(v);
386
7a0525c7 387 g_free(siv);
a020f980
PB
388}
389
7a0525c7 390Visitor *string_input_visitor_new(const char *str)
a020f980
PB
391{
392 StringInputVisitor *v;
393
f332e830 394 assert(str);
a020f980
PB
395 v = g_malloc0(sizeof(*v));
396
983f52d4 397 v->visitor.type = VISITOR_INPUT;
4c40314a 398 v->visitor.type_int64 = parse_type_int64;
f755dea7 399 v->visitor.type_uint64 = parse_type_uint64;
a5829ccf 400 v->visitor.type_size = parse_type_size;
a020f980
PB
401 v->visitor.type_bool = parse_type_bool;
402 v->visitor.type_str = parse_type_str;
403 v->visitor.type_number = parse_type_number;
a7333712 404 v->visitor.type_null = parse_type_null;
659268ff
HT
405 v->visitor.start_list = start_list;
406 v->visitor.next_list = next_list;
a4a1c70d 407 v->visitor.check_list = check_list;
659268ff 408 v->visitor.end_list = end_list;
2c0ef9f4 409 v->visitor.free = string_input_free;
a020f980
PB
410
411 v->string = str;
c9fba9de 412 v->lm = LM_NONE;
7a0525c7 413 return &v->visitor;
a020f980 414}