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