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