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