]>
Commit | Line | Data |
---|---|---|
a020f980 PB |
1 | /* |
2 | * String parsing visitor | |
3 | * | |
08f9541d | 4 | * Copyright Red Hat, Inc. 2012-2016 |
a020f980 PB |
5 | * |
6 | * Author: Paolo Bonzini <[email protected]> | |
7 | * | |
8 | * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. | |
9 | * See the COPYING.LIB file in the top-level directory. | |
10 | * | |
11 | */ | |
12 | ||
cbf21151 | 13 | #include "qemu/osdep.h" |
da34e65c | 14 | #include "qapi/error.h" |
a020f980 | 15 | #include "qemu-common.h" |
7b1b5d19 PB |
16 | #include "qapi/string-input-visitor.h" |
17 | #include "qapi/visitor-impl.h" | |
18 | #include "qapi/qmp/qerror.h" | |
84be629d | 19 | #include "qapi/qmp/qnull.h" |
a5829ccf | 20 | #include "qemu/option.h" |
659268ff HT |
21 | #include "qemu/queue.h" |
22 | #include "qemu/range.h" | |
23 | ||
a020f980 PB |
24 | |
25 | struct StringInputVisitor | |
26 | { | |
27 | Visitor visitor; | |
659268ff | 28 | |
659268ff HT |
29 | GList *ranges; |
30 | GList *cur_range; | |
31 | int64_t cur; | |
32 | ||
a020f980 | 33 | const char *string; |
1158bb2a | 34 | void *list; /* Only needed for sanity checking the caller */ |
a020f980 PB |
35 | }; |
36 | ||
d7bea75d EB |
37 | static StringInputVisitor *to_siv(Visitor *v) |
38 | { | |
39 | return container_of(v, StringInputVisitor, visitor); | |
40 | } | |
41 | ||
0d156683 MT |
42 | static void free_range(void *range, void *dummy) |
43 | { | |
44 | g_free(range); | |
45 | } | |
46 | ||
74f24cb6 | 47 | static int parse_str(StringInputVisitor *siv, const char *name, Error **errp) |
659268ff HT |
48 | { |
49 | char *str = (char *) siv->string; | |
50 | long long start, end; | |
51 | Range *cur; | |
52 | char *endptr; | |
53 | ||
54 | if (siv->ranges) { | |
74f24cb6 | 55 | return 0; |
659268ff HT |
56 | } |
57 | ||
d2788227 MA |
58 | if (!*str) { |
59 | return 0; | |
60 | } | |
61 | ||
659268ff | 62 | do { |
c210ee95 | 63 | errno = 0; |
659268ff | 64 | start = strtoll(str, &endptr, 0); |
c210ee95 | 65 | if (errno == 0 && endptr > str) { |
659268ff HT |
66 | if (*endptr == '\0') { |
67 | cur = g_malloc0(sizeof(*cur)); | |
a0efbf16 | 68 | range_set_bounds(cur, start, start); |
7c47959d | 69 | siv->ranges = range_list_insert(siv->ranges, cur); |
659268ff HT |
70 | cur = NULL; |
71 | str = NULL; | |
72 | } else if (*endptr == '-') { | |
73 | str = endptr + 1; | |
c210ee95 | 74 | errno = 0; |
659268ff | 75 | end = strtoll(str, &endptr, 0); |
c210ee95 | 76 | if (errno == 0 && endptr > str && start <= end && |
659268ff HT |
77 | (start > INT64_MAX - 65536 || |
78 | end < start + 65536)) { | |
79 | if (*endptr == '\0') { | |
80 | cur = g_malloc0(sizeof(*cur)); | |
a0efbf16 | 81 | range_set_bounds(cur, start, end); |
7c47959d | 82 | siv->ranges = range_list_insert(siv->ranges, cur); |
659268ff HT |
83 | cur = NULL; |
84 | str = NULL; | |
85 | } else if (*endptr == ',') { | |
86 | str = endptr + 1; | |
87 | cur = g_malloc0(sizeof(*cur)); | |
a0efbf16 | 88 | range_set_bounds(cur, start, end); |
7c47959d | 89 | siv->ranges = range_list_insert(siv->ranges, cur); |
659268ff HT |
90 | cur = NULL; |
91 | } else { | |
92 | goto error; | |
93 | } | |
94 | } else { | |
95 | goto error; | |
96 | } | |
97 | } else if (*endptr == ',') { | |
98 | str = endptr + 1; | |
99 | cur = g_malloc0(sizeof(*cur)); | |
a0efbf16 | 100 | range_set_bounds(cur, start, start); |
7c47959d | 101 | siv->ranges = range_list_insert(siv->ranges, cur); |
659268ff HT |
102 | cur = NULL; |
103 | } else { | |
104 | goto error; | |
105 | } | |
106 | } else { | |
107 | goto error; | |
108 | } | |
109 | } while (str); | |
110 | ||
74f24cb6 | 111 | return 0; |
659268ff | 112 | error: |
0d156683 MT |
113 | g_list_foreach(siv->ranges, free_range, NULL); |
114 | g_list_free(siv->ranges); | |
115 | siv->ranges = NULL; | |
74f24cb6 EB |
116 | error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", |
117 | "an int64 value or range"); | |
118 | return -1; | |
659268ff HT |
119 | } |
120 | ||
121 | static void | |
d9f62dde EB |
122 | start_list(Visitor *v, const char *name, GenericList **list, size_t size, |
123 | Error **errp) | |
659268ff | 124 | { |
d7bea75d | 125 | StringInputVisitor *siv = to_siv(v); |
659268ff | 126 | |
d9f62dde EB |
127 | /* We don't support visits without a list */ |
128 | assert(list); | |
1158bb2a | 129 | siv->list = list; |
d9f62dde | 130 | |
74f24cb6 | 131 | if (parse_str(siv, name, errp) < 0) { |
d9f62dde | 132 | *list = NULL; |
74f24cb6 EB |
133 | return; |
134 | } | |
659268ff HT |
135 | |
136 | siv->cur_range = g_list_first(siv->ranges); | |
137 | if (siv->cur_range) { | |
138 | Range *r = siv->cur_range->data; | |
139 | if (r) { | |
a0efbf16 | 140 | siv->cur = range_lob(r); |
659268ff | 141 | } |
d9f62dde EB |
142 | *list = g_malloc0(size); |
143 | } else { | |
144 | *list = NULL; | |
659268ff HT |
145 | } |
146 | } | |
147 | ||
d9f62dde | 148 | static GenericList *next_list(Visitor *v, GenericList *tail, size_t size) |
659268ff | 149 | { |
d7bea75d | 150 | StringInputVisitor *siv = to_siv(v); |
659268ff HT |
151 | Range *r; |
152 | ||
153 | if (!siv->ranges || !siv->cur_range) { | |
154 | return NULL; | |
155 | } | |
156 | ||
157 | r = siv->cur_range->data; | |
158 | if (!r) { | |
159 | return NULL; | |
160 | } | |
161 | ||
a0efbf16 | 162 | if (!range_contains(r, siv->cur)) { |
659268ff HT |
163 | siv->cur_range = g_list_next(siv->cur_range); |
164 | if (!siv->cur_range) { | |
165 | return NULL; | |
166 | } | |
167 | r = siv->cur_range->data; | |
168 | if (!r) { | |
169 | return NULL; | |
170 | } | |
a0efbf16 | 171 | siv->cur = range_lob(r); |
659268ff HT |
172 | } |
173 | ||
d9f62dde EB |
174 | tail->next = g_malloc0(size); |
175 | return tail->next; | |
659268ff HT |
176 | } |
177 | ||
a4a1c70d MA |
178 | static void check_list(Visitor *v, Error **errp) |
179 | { | |
180 | const StringInputVisitor *siv = to_siv(v); | |
181 | Range *r; | |
182 | GList *cur_range; | |
183 | ||
184 | if (!siv->ranges || !siv->cur_range) { | |
185 | return; | |
186 | } | |
187 | ||
188 | r = siv->cur_range->data; | |
189 | if (!r) { | |
190 | return; | |
191 | } | |
192 | ||
193 | if (!range_contains(r, siv->cur)) { | |
194 | cur_range = g_list_next(siv->cur_range); | |
195 | if (!cur_range) { | |
196 | return; | |
197 | } | |
198 | r = cur_range->data; | |
199 | if (!r) { | |
200 | return; | |
201 | } | |
202 | } | |
203 | ||
204 | error_setg(errp, "Range contains too many values"); | |
205 | } | |
206 | ||
1158bb2a | 207 | static void end_list(Visitor *v, void **obj) |
659268ff | 208 | { |
1158bb2a EB |
209 | StringInputVisitor *siv = to_siv(v); |
210 | ||
211 | assert(siv->list == obj); | |
659268ff HT |
212 | } |
213 | ||
0b2a0d6b | 214 | static void parse_type_int64(Visitor *v, const char *name, int64_t *obj, |
4c40314a | 215 | Error **errp) |
a020f980 | 216 | { |
d7bea75d | 217 | StringInputVisitor *siv = to_siv(v); |
a020f980 | 218 | |
74f24cb6 EB |
219 | if (parse_str(siv, name, errp) < 0) { |
220 | return; | |
221 | } | |
659268ff HT |
222 | |
223 | if (!siv->ranges) { | |
224 | goto error; | |
225 | } | |
226 | ||
227 | if (!siv->cur_range) { | |
228 | Range *r; | |
229 | ||
230 | siv->cur_range = g_list_first(siv->ranges); | |
231 | if (!siv->cur_range) { | |
232 | goto error; | |
233 | } | |
234 | ||
235 | r = siv->cur_range->data; | |
236 | if (!r) { | |
237 | goto error; | |
238 | } | |
239 | ||
a0efbf16 | 240 | siv->cur = range_lob(r); |
659268ff HT |
241 | } |
242 | ||
243 | *obj = siv->cur; | |
244 | siv->cur++; | |
245 | return; | |
246 | ||
247 | error: | |
0a40bdab | 248 | error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", |
c6bd8c70 | 249 | "an int64 value or range"); |
a020f980 PB |
250 | } |
251 | ||
0b2a0d6b | 252 | static void parse_type_uint64(Visitor *v, const char *name, uint64_t *obj, |
f755dea7 EB |
253 | Error **errp) |
254 | { | |
255 | /* FIXME: parse_type_int64 mishandles values over INT64_MAX */ | |
256 | int64_t i; | |
257 | Error *err = NULL; | |
0b2a0d6b | 258 | parse_type_int64(v, name, &i, &err); |
f755dea7 EB |
259 | if (err) { |
260 | error_propagate(errp, err); | |
261 | } else { | |
262 | *obj = i; | |
263 | } | |
264 | } | |
265 | ||
0b2a0d6b | 266 | static void parse_type_size(Visitor *v, const char *name, uint64_t *obj, |
a5829ccf PB |
267 | Error **errp) |
268 | { | |
d7bea75d | 269 | StringInputVisitor *siv = to_siv(v); |
a5829ccf PB |
270 | Error *err = NULL; |
271 | uint64_t val; | |
272 | ||
f332e830 | 273 | parse_option_size(name, siv->string, &val, &err); |
a5829ccf PB |
274 | if (err) { |
275 | error_propagate(errp, err); | |
276 | return; | |
277 | } | |
278 | ||
279 | *obj = val; | |
280 | } | |
281 | ||
0b2a0d6b | 282 | static void parse_type_bool(Visitor *v, const char *name, bool *obj, |
a020f980 PB |
283 | Error **errp) |
284 | { | |
d7bea75d | 285 | StringInputVisitor *siv = to_siv(v); |
a020f980 | 286 | |
f332e830 MA |
287 | if (!strcasecmp(siv->string, "on") || |
288 | !strcasecmp(siv->string, "yes") || | |
289 | !strcasecmp(siv->string, "true")) { | |
290 | *obj = true; | |
291 | return; | |
292 | } | |
293 | if (!strcasecmp(siv->string, "off") || | |
294 | !strcasecmp(siv->string, "no") || | |
295 | !strcasecmp(siv->string, "false")) { | |
296 | *obj = false; | |
297 | return; | |
a020f980 PB |
298 | } |
299 | ||
c6bd8c70 MA |
300 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", |
301 | "boolean"); | |
a020f980 PB |
302 | } |
303 | ||
0b2a0d6b | 304 | static void parse_type_str(Visitor *v, const char *name, char **obj, |
a020f980 PB |
305 | Error **errp) |
306 | { | |
d7bea75d | 307 | StringInputVisitor *siv = to_siv(v); |
f332e830 MA |
308 | |
309 | *obj = g_strdup(siv->string); | |
a020f980 PB |
310 | } |
311 | ||
0b2a0d6b | 312 | static void parse_type_number(Visitor *v, const char *name, double *obj, |
a020f980 PB |
313 | Error **errp) |
314 | { | |
d7bea75d | 315 | StringInputVisitor *siv = to_siv(v); |
a020f980 PB |
316 | char *endp = (char *) siv->string; |
317 | double val; | |
318 | ||
319 | errno = 0; | |
f332e830 MA |
320 | val = strtod(siv->string, &endp); |
321 | if (errno || endp == siv->string || *endp) { | |
c6bd8c70 MA |
322 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", |
323 | "number"); | |
a020f980 PB |
324 | return; |
325 | } | |
326 | ||
327 | *obj = val; | |
328 | } | |
329 | ||
d2f95f4d MA |
330 | static void parse_type_null(Visitor *v, const char *name, QNull **obj, |
331 | Error **errp) | |
a7333712 GK |
332 | { |
333 | StringInputVisitor *siv = to_siv(v); | |
334 | ||
d2f95f4d MA |
335 | *obj = NULL; |
336 | ||
a7333712 GK |
337 | if (!siv->string || siv->string[0]) { |
338 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", | |
339 | "null"); | |
d2f95f4d | 340 | return; |
a7333712 | 341 | } |
d2f95f4d MA |
342 | |
343 | *obj = qnull(); | |
a7333712 GK |
344 | } |
345 | ||
2c0ef9f4 EB |
346 | static void string_input_free(Visitor *v) |
347 | { | |
348 | StringInputVisitor *siv = to_siv(v); | |
349 | ||
7a0525c7 EB |
350 | g_list_foreach(siv->ranges, free_range, NULL); |
351 | g_list_free(siv->ranges); | |
352 | g_free(siv); | |
a020f980 PB |
353 | } |
354 | ||
7a0525c7 | 355 | Visitor *string_input_visitor_new(const char *str) |
a020f980 PB |
356 | { |
357 | StringInputVisitor *v; | |
358 | ||
f332e830 | 359 | assert(str); |
a020f980 PB |
360 | v = g_malloc0(sizeof(*v)); |
361 | ||
983f52d4 | 362 | v->visitor.type = VISITOR_INPUT; |
4c40314a | 363 | v->visitor.type_int64 = parse_type_int64; |
f755dea7 | 364 | v->visitor.type_uint64 = parse_type_uint64; |
a5829ccf | 365 | v->visitor.type_size = parse_type_size; |
a020f980 PB |
366 | v->visitor.type_bool = parse_type_bool; |
367 | v->visitor.type_str = parse_type_str; | |
368 | v->visitor.type_number = parse_type_number; | |
a7333712 | 369 | v->visitor.type_null = parse_type_null; |
659268ff HT |
370 | v->visitor.start_list = start_list; |
371 | v->visitor.next_list = next_list; | |
a4a1c70d | 372 | v->visitor.check_list = check_list; |
659268ff | 373 | v->visitor.end_list = end_list; |
2c0ef9f4 | 374 | v->visitor.free = string_input_free; |
a020f980 PB |
375 | |
376 | v->string = str; | |
7a0525c7 | 377 | return &v->visitor; |
a020f980 | 378 | } |