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