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