monitor/qmp: Update comment for commit 4eaca8de268
[qemu/armbru.git] / qapi / string-input-visitor.c
blob9be418b6d61c078cc882686d197c1238bf02e4c0
1 /*
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 "qapi/string-input-visitor.h"
16 #include "qapi/visitor-impl.h"
17 #include "qapi/qmp/qerror.h"
18 #include "qapi/qmp/qnull.h"
19 #include "qemu/option.h"
20 #include "qemu/cutils.h"
22 typedef 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;
35 /* protect against DOS attacks, limit the amount of elements per range */
36 #define RANGE_MAX_ELEMENTS 65536
38 typedef union RangeElement {
39 int64_t i64;
40 uint64_t u64;
41 } RangeElement;
43 struct StringInputVisitor
45 Visitor visitor;
47 /* List parsing state */
48 ListMode lm;
49 RangeElement rangeNext;
50 RangeElement rangeEnd;
51 const char *unparsed_string;
52 void *list;
54 /* The original string to parse */
55 const char *string;
58 static StringInputVisitor *to_siv(Visitor *v)
60 return container_of(v, StringInputVisitor, visitor);
63 static void start_list(Visitor *v, const char *name, GenericList **list,
64 size_t size, Error **errp)
66 StringInputVisitor *siv = to_siv(v);
68 assert(siv->lm == LM_NONE);
69 siv->list = list;
70 siv->unparsed_string = siv->string;
72 if (!siv->string[0]) {
73 if (list) {
74 *list = NULL;
76 siv->lm = LM_END;
77 } else {
78 if (list) {
79 *list = g_malloc0(size);
81 siv->lm = LM_UNPARSED;
85 static GenericList *next_list(Visitor *v, GenericList *tail, size_t size)
87 StringInputVisitor *siv = to_siv(v);
89 switch (siv->lm) {
90 case LM_END:
91 return NULL;
92 case LM_INT64_RANGE:
93 case LM_UINT64_RANGE:
94 case LM_UNPARSED:
95 /* we have an unparsed string or something left in a range */
96 break;
97 default:
98 abort();
101 tail->next = g_malloc0(size);
102 return tail->next;
105 static void check_list(Visitor *v, Error **errp)
107 const StringInputVisitor *siv = to_siv(v);
109 switch (siv->lm) {
110 case LM_INT64_RANGE:
111 case LM_UINT64_RANGE:
112 case LM_UNPARSED:
113 error_setg(errp, "Fewer list elements expected");
114 return;
115 case LM_END:
116 return;
117 default:
118 abort();
122 static void end_list(Visitor *v, void **obj)
124 StringInputVisitor *siv = to_siv(v);
126 assert(siv->lm != LM_NONE);
127 assert(siv->list == obj);
128 siv->list = NULL;
129 siv->unparsed_string = NULL;
130 siv->lm = LM_NONE;
133 static int try_parse_int64_list_entry(StringInputVisitor *siv, int64_t *obj)
135 const char *endptr;
136 int64_t start, end;
138 /* parse a simple int64 or range */
139 if (qemu_strtoi64(siv->unparsed_string, &endptr, 0, &start)) {
140 return -EINVAL;
142 end = start;
144 switch (endptr[0]) {
145 case '\0':
146 siv->unparsed_string = endptr;
147 break;
148 case ',':
149 siv->unparsed_string = endptr + 1;
150 break;
151 case '-':
152 /* parse the end of the range */
153 if (qemu_strtoi64(endptr + 1, &endptr, 0, &end)) {
154 return -EINVAL;
156 if (start > end || end - start >= RANGE_MAX_ELEMENTS) {
157 return -EINVAL;
159 switch (endptr[0]) {
160 case '\0':
161 siv->unparsed_string = endptr;
162 break;
163 case ',':
164 siv->unparsed_string = endptr + 1;
165 break;
166 default:
167 return -EINVAL;
169 break;
170 default:
171 return -EINVAL;
174 /* we have a proper range (with maybe only one element) */
175 siv->lm = LM_INT64_RANGE;
176 siv->rangeNext.i64 = start;
177 siv->rangeEnd.i64 = end;
178 return 0;
181 static void parse_type_int64(Visitor *v, const char *name, int64_t *obj,
182 Error **errp)
184 StringInputVisitor *siv = to_siv(v);
185 int64_t val;
187 switch (siv->lm) {
188 case LM_NONE:
189 /* just parse a simple int64, bail out if not completely consumed */
190 if (qemu_strtoi64(siv->string, NULL, 0, &val)) {
191 error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
192 name ? name : "null", "int64");
193 return;
195 *obj = val;
196 return;
197 case LM_UNPARSED:
198 if (try_parse_int64_list_entry(siv, obj)) {
199 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
200 "list of int64 values or ranges");
201 return;
203 assert(siv->lm == LM_INT64_RANGE);
204 /* fall through */
205 case LM_INT64_RANGE:
206 /* return the next element in the range */
207 assert(siv->rangeNext.i64 <= siv->rangeEnd.i64);
208 *obj = siv->rangeNext.i64++;
210 if (siv->rangeNext.i64 > siv->rangeEnd.i64 || *obj == INT64_MAX) {
211 /* end of range, check if there is more to parse */
212 siv->lm = siv->unparsed_string[0] ? LM_UNPARSED : LM_END;
214 return;
215 case LM_END:
216 error_setg(errp, "Fewer list elements expected");
217 return;
218 default:
219 abort();
223 static int try_parse_uint64_list_entry(StringInputVisitor *siv, uint64_t *obj)
225 const char *endptr;
226 uint64_t start, end;
228 /* parse a simple uint64 or range */
229 if (qemu_strtou64(siv->unparsed_string, &endptr, 0, &start)) {
230 return -EINVAL;
232 end = start;
234 switch (endptr[0]) {
235 case '\0':
236 siv->unparsed_string = endptr;
237 break;
238 case ',':
239 siv->unparsed_string = endptr + 1;
240 break;
241 case '-':
242 /* parse the end of the range */
243 if (qemu_strtou64(endptr + 1, &endptr, 0, &end)) {
244 return -EINVAL;
246 if (start > end || end - start >= RANGE_MAX_ELEMENTS) {
247 return -EINVAL;
249 switch (endptr[0]) {
250 case '\0':
251 siv->unparsed_string = endptr;
252 break;
253 case ',':
254 siv->unparsed_string = endptr + 1;
255 break;
256 default:
257 return -EINVAL;
259 break;
260 default:
261 return -EINVAL;
264 /* we have a proper range (with maybe only one element) */
265 siv->lm = LM_UINT64_RANGE;
266 siv->rangeNext.u64 = start;
267 siv->rangeEnd.u64 = end;
268 return 0;
271 static void parse_type_uint64(Visitor *v, const char *name, uint64_t *obj,
272 Error **errp)
274 StringInputVisitor *siv = to_siv(v);
275 uint64_t val;
277 switch (siv->lm) {
278 case LM_NONE:
279 /* just parse a simple uint64, bail out if not completely consumed */
280 if (qemu_strtou64(siv->string, NULL, 0, &val)) {
281 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
282 "uint64");
283 return;
285 *obj = val;
286 return;
287 case LM_UNPARSED:
288 if (try_parse_uint64_list_entry(siv, obj)) {
289 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null",
290 "list of uint64 values or ranges");
291 return;
293 assert(siv->lm == LM_UINT64_RANGE);
294 /* fall through */
295 case LM_UINT64_RANGE:
296 /* return the next element in the range */
297 assert(siv->rangeNext.u64 <= siv->rangeEnd.u64);
298 *obj = siv->rangeNext.u64++;
300 if (siv->rangeNext.u64 > siv->rangeEnd.u64 || *obj == UINT64_MAX) {
301 /* end of range, check if there is more to parse */
302 siv->lm = siv->unparsed_string[0] ? LM_UNPARSED : LM_END;
304 return;
305 case LM_END:
306 error_setg(errp, "Fewer list elements expected");
307 return;
308 default:
309 abort();
313 static void parse_type_size(Visitor *v, const char *name, uint64_t *obj,
314 Error **errp)
316 StringInputVisitor *siv = to_siv(v);
317 Error *err = NULL;
318 uint64_t val;
320 assert(siv->lm == LM_NONE);
321 parse_option_size(name, siv->string, &val, &err);
322 if (err) {
323 error_propagate(errp, err);
324 return;
327 *obj = val;
330 static void parse_type_bool(Visitor *v, const char *name, bool *obj,
331 Error **errp)
333 StringInputVisitor *siv = to_siv(v);
335 assert(siv->lm == LM_NONE);
336 if (!strcasecmp(siv->string, "on") ||
337 !strcasecmp(siv->string, "yes") ||
338 !strcasecmp(siv->string, "true")) {
339 *obj = true;
340 return;
342 if (!strcasecmp(siv->string, "off") ||
343 !strcasecmp(siv->string, "no") ||
344 !strcasecmp(siv->string, "false")) {
345 *obj = false;
346 return;
349 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
350 "boolean");
353 static void parse_type_str(Visitor *v, const char *name, char **obj,
354 Error **errp)
356 StringInputVisitor *siv = to_siv(v);
358 assert(siv->lm == LM_NONE);
359 *obj = g_strdup(siv->string);
362 static void parse_type_number(Visitor *v, const char *name, double *obj,
363 Error **errp)
365 StringInputVisitor *siv = to_siv(v);
366 double val;
368 assert(siv->lm == LM_NONE);
369 if (qemu_strtod_finite(siv->string, NULL, &val)) {
370 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
371 "number");
372 return;
375 *obj = val;
378 static void parse_type_null(Visitor *v, const char *name, QNull **obj,
379 Error **errp)
381 StringInputVisitor *siv = to_siv(v);
383 assert(siv->lm == LM_NONE);
384 *obj = NULL;
386 if (siv->string[0]) {
387 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
388 "null");
389 return;
392 *obj = qnull();
395 static void string_input_free(Visitor *v)
397 StringInputVisitor *siv = to_siv(v);
399 g_free(siv);
402 Visitor *string_input_visitor_new(const char *str)
404 StringInputVisitor *v;
406 assert(str);
407 v = g_malloc0(sizeof(*v));
409 v->visitor.type = VISITOR_INPUT;
410 v->visitor.type_int64 = parse_type_int64;
411 v->visitor.type_uint64 = parse_type_uint64;
412 v->visitor.type_size = parse_type_size;
413 v->visitor.type_bool = parse_type_bool;
414 v->visitor.type_str = parse_type_str;
415 v->visitor.type_number = parse_type_number;
416 v->visitor.type_null = parse_type_null;
417 v->visitor.start_list = start_list;
418 v->visitor.next_list = next_list;
419 v->visitor.check_list = check_list;
420 v->visitor.end_list = end_list;
421 v->visitor.free = string_input_free;
423 v->string = str;
424 v->lm = LM_NONE;
425 return &v->visitor;