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