]>
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 <pbonzini@redhat.com> | |
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 | ||
a4a1c70d MA |
173 | static void check_list(Visitor *v, Error **errp) |
174 | { | |
175 | const StringInputVisitor *siv = to_siv(v); | |
176 | Range *r; | |
177 | GList *cur_range; | |
178 | ||
179 | if (!siv->ranges || !siv->cur_range) { | |
180 | return; | |
181 | } | |
182 | ||
183 | r = siv->cur_range->data; | |
184 | if (!r) { | |
185 | return; | |
186 | } | |
187 | ||
188 | if (!range_contains(r, siv->cur)) { | |
189 | cur_range = g_list_next(siv->cur_range); | |
190 | if (!cur_range) { | |
191 | return; | |
192 | } | |
193 | r = cur_range->data; | |
194 | if (!r) { | |
195 | return; | |
196 | } | |
197 | } | |
198 | ||
199 | error_setg(errp, "Range contains too many values"); | |
200 | } | |
201 | ||
1158bb2a | 202 | static void end_list(Visitor *v, void **obj) |
659268ff | 203 | { |
1158bb2a EB |
204 | StringInputVisitor *siv = to_siv(v); |
205 | ||
206 | assert(siv->list == obj); | |
659268ff HT |
207 | } |
208 | ||
0b2a0d6b | 209 | static void parse_type_int64(Visitor *v, const char *name, int64_t *obj, |
4c40314a | 210 | Error **errp) |
a020f980 | 211 | { |
d7bea75d | 212 | StringInputVisitor *siv = to_siv(v); |
a020f980 | 213 | |
74f24cb6 EB |
214 | if (parse_str(siv, name, errp) < 0) { |
215 | return; | |
216 | } | |
659268ff HT |
217 | |
218 | if (!siv->ranges) { | |
219 | goto error; | |
220 | } | |
221 | ||
222 | if (!siv->cur_range) { | |
223 | Range *r; | |
224 | ||
225 | siv->cur_range = g_list_first(siv->ranges); | |
226 | if (!siv->cur_range) { | |
227 | goto error; | |
228 | } | |
229 | ||
230 | r = siv->cur_range->data; | |
231 | if (!r) { | |
232 | goto error; | |
233 | } | |
234 | ||
a0efbf16 | 235 | siv->cur = range_lob(r); |
659268ff HT |
236 | } |
237 | ||
238 | *obj = siv->cur; | |
239 | siv->cur++; | |
240 | return; | |
241 | ||
242 | error: | |
0a40bdab | 243 | error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", |
c6bd8c70 | 244 | "an int64 value or range"); |
a020f980 PB |
245 | } |
246 | ||
0b2a0d6b | 247 | static void parse_type_uint64(Visitor *v, const char *name, uint64_t *obj, |
f755dea7 EB |
248 | Error **errp) |
249 | { | |
250 | /* FIXME: parse_type_int64 mishandles values over INT64_MAX */ | |
251 | int64_t i; | |
252 | Error *err = NULL; | |
0b2a0d6b | 253 | parse_type_int64(v, name, &i, &err); |
f755dea7 EB |
254 | if (err) { |
255 | error_propagate(errp, err); | |
256 | } else { | |
257 | *obj = i; | |
258 | } | |
259 | } | |
260 | ||
0b2a0d6b | 261 | static void parse_type_size(Visitor *v, const char *name, uint64_t *obj, |
a5829ccf PB |
262 | Error **errp) |
263 | { | |
d7bea75d | 264 | StringInputVisitor *siv = to_siv(v); |
a5829ccf PB |
265 | Error *err = NULL; |
266 | uint64_t val; | |
267 | ||
f332e830 | 268 | parse_option_size(name, siv->string, &val, &err); |
a5829ccf PB |
269 | if (err) { |
270 | error_propagate(errp, err); | |
271 | return; | |
272 | } | |
273 | ||
274 | *obj = val; | |
275 | } | |
276 | ||
0b2a0d6b | 277 | static void parse_type_bool(Visitor *v, const char *name, bool *obj, |
a020f980 PB |
278 | Error **errp) |
279 | { | |
d7bea75d | 280 | StringInputVisitor *siv = to_siv(v); |
a020f980 | 281 | |
f332e830 MA |
282 | if (!strcasecmp(siv->string, "on") || |
283 | !strcasecmp(siv->string, "yes") || | |
284 | !strcasecmp(siv->string, "true")) { | |
285 | *obj = true; | |
286 | return; | |
287 | } | |
288 | if (!strcasecmp(siv->string, "off") || | |
289 | !strcasecmp(siv->string, "no") || | |
290 | !strcasecmp(siv->string, "false")) { | |
291 | *obj = false; | |
292 | return; | |
a020f980 PB |
293 | } |
294 | ||
c6bd8c70 MA |
295 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", |
296 | "boolean"); | |
a020f980 PB |
297 | } |
298 | ||
0b2a0d6b | 299 | static void parse_type_str(Visitor *v, const char *name, char **obj, |
a020f980 PB |
300 | Error **errp) |
301 | { | |
d7bea75d | 302 | StringInputVisitor *siv = to_siv(v); |
f332e830 MA |
303 | |
304 | *obj = g_strdup(siv->string); | |
a020f980 PB |
305 | } |
306 | ||
0b2a0d6b | 307 | static void parse_type_number(Visitor *v, const char *name, double *obj, |
a020f980 PB |
308 | Error **errp) |
309 | { | |
d7bea75d | 310 | StringInputVisitor *siv = to_siv(v); |
a020f980 PB |
311 | char *endp = (char *) siv->string; |
312 | double val; | |
313 | ||
314 | errno = 0; | |
f332e830 MA |
315 | val = strtod(siv->string, &endp); |
316 | if (errno || endp == siv->string || *endp) { | |
c6bd8c70 MA |
317 | error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", |
318 | "number"); | |
a020f980 PB |
319 | return; |
320 | } | |
321 | ||
322 | *obj = val; | |
323 | } | |
324 | ||
2c0ef9f4 EB |
325 | static void string_input_free(Visitor *v) |
326 | { | |
327 | StringInputVisitor *siv = to_siv(v); | |
328 | ||
7a0525c7 EB |
329 | g_list_foreach(siv->ranges, free_range, NULL); |
330 | g_list_free(siv->ranges); | |
331 | g_free(siv); | |
a020f980 PB |
332 | } |
333 | ||
7a0525c7 | 334 | Visitor *string_input_visitor_new(const char *str) |
a020f980 PB |
335 | { |
336 | StringInputVisitor *v; | |
337 | ||
f332e830 | 338 | assert(str); |
a020f980 PB |
339 | v = g_malloc0(sizeof(*v)); |
340 | ||
983f52d4 | 341 | v->visitor.type = VISITOR_INPUT; |
4c40314a | 342 | v->visitor.type_int64 = parse_type_int64; |
f755dea7 | 343 | v->visitor.type_uint64 = parse_type_uint64; |
a5829ccf | 344 | v->visitor.type_size = parse_type_size; |
a020f980 PB |
345 | v->visitor.type_bool = parse_type_bool; |
346 | v->visitor.type_str = parse_type_str; | |
347 | v->visitor.type_number = parse_type_number; | |
659268ff HT |
348 | v->visitor.start_list = start_list; |
349 | v->visitor.next_list = next_list; | |
a4a1c70d | 350 | v->visitor.check_list = check_list; |
659268ff | 351 | v->visitor.end_list = end_list; |
2c0ef9f4 | 352 | v->visitor.free = string_input_free; |
a020f980 PB |
353 | |
354 | v->string = str; | |
7a0525c7 | 355 | return &v->visitor; |
a020f980 | 356 | } |