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