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