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