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