xref: /openbmc/qemu/qapi/string-input-visitor.c (revision 10358b6a)
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-common.h"
14 #include "qapi/string-input-visitor.h"
15 #include "qapi/visitor-impl.h"
16 #include "qapi/qmp/qerror.h"
17 #include "qemu/option.h"
18 #include "qemu/queue.h"
19 #include "qemu/range.h"
20 
21 
22 struct StringInputVisitor
23 {
24     Visitor visitor;
25 
26     bool head;
27 
28     GList *ranges;
29     GList *cur_range;
30     int64_t cur;
31 
32     const char *string;
33 };
34 
35 static void free_range(void *range, void *dummy)
36 {
37     g_free(range);
38 }
39 
40 static void parse_str(StringInputVisitor *siv, Error **errp)
41 {
42     char *str = (char *) siv->string;
43     long long start, end;
44     Range *cur;
45     char *endptr;
46 
47     if (siv->ranges) {
48         return;
49     }
50 
51     do {
52         errno = 0;
53         start = strtoll(str, &endptr, 0);
54         if (errno == 0 && endptr > str) {
55             if (*endptr == '\0') {
56                 cur = g_malloc0(sizeof(*cur));
57                 cur->begin = start;
58                 cur->end = start + 1;
59                 siv->ranges = g_list_insert_sorted_merged(siv->ranges, cur,
60                                                           range_compare);
61                 cur = NULL;
62                 str = NULL;
63             } else if (*endptr == '-') {
64                 str = endptr + 1;
65                 errno = 0;
66                 end = strtoll(str, &endptr, 0);
67                 if (errno == 0 && endptr > str && start <= end &&
68                     (start > INT64_MAX - 65536 ||
69                      end < start + 65536)) {
70                     if (*endptr == '\0') {
71                         cur = g_malloc0(sizeof(*cur));
72                         cur->begin = start;
73                         cur->end = end + 1;
74                         siv->ranges =
75                             g_list_insert_sorted_merged(siv->ranges,
76                                                         cur,
77                                                         range_compare);
78                         cur = NULL;
79                         str = NULL;
80                     } else if (*endptr == ',') {
81                         str = endptr + 1;
82                         cur = g_malloc0(sizeof(*cur));
83                         cur->begin = start;
84                         cur->end = end + 1;
85                         siv->ranges =
86                             g_list_insert_sorted_merged(siv->ranges,
87                                                         cur,
88                                                         range_compare);
89                         cur = NULL;
90                     } else {
91                         goto error;
92                     }
93                 } else {
94                     goto error;
95                 }
96             } else if (*endptr == ',') {
97                 str = endptr + 1;
98                 cur = g_malloc0(sizeof(*cur));
99                 cur->begin = start;
100                 cur->end = start + 1;
101                 siv->ranges = g_list_insert_sorted_merged(siv->ranges,
102                                                           cur,
103                                                           range_compare);
104                 cur = NULL;
105             } else {
106                 goto error;
107             }
108         } else {
109             goto error;
110         }
111     } while (str);
112 
113     return;
114 error:
115     g_list_foreach(siv->ranges, free_range, NULL);
116     g_list_free(siv->ranges);
117     siv->ranges = NULL;
118 }
119 
120 static void
121 start_list(Visitor *v, const char *name, Error **errp)
122 {
123     StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
124 
125     parse_str(siv, errp);
126 
127     siv->cur_range = g_list_first(siv->ranges);
128     if (siv->cur_range) {
129         Range *r = siv->cur_range->data;
130         if (r) {
131             siv->cur = r->begin;
132         }
133     }
134 }
135 
136 static GenericList *
137 next_list(Visitor *v, GenericList **list, Error **errp)
138 {
139     StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
140     GenericList **link;
141     Range *r;
142 
143     if (!siv->ranges || !siv->cur_range) {
144         return NULL;
145     }
146 
147     r = siv->cur_range->data;
148     if (!r) {
149         return NULL;
150     }
151 
152     if (siv->cur < r->begin || siv->cur >= r->end) {
153         siv->cur_range = g_list_next(siv->cur_range);
154         if (!siv->cur_range) {
155             return NULL;
156         }
157         r = siv->cur_range->data;
158         if (!r) {
159             return NULL;
160         }
161         siv->cur = r->begin;
162     }
163 
164     if (siv->head) {
165         link = list;
166         siv->head = false;
167     } else {
168         link = &(*list)->next;
169     }
170 
171     *link = g_malloc0(sizeof **link);
172     return *link;
173 }
174 
175 static void
176 end_list(Visitor *v, Error **errp)
177 {
178     StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
179     siv->head = true;
180 }
181 
182 static void parse_type_int(Visitor *v, int64_t *obj, const char *name,
183                            Error **errp)
184 {
185     StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
186 
187     if (!siv->string) {
188         error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
189                   "integer");
190         return;
191     }
192 
193     parse_str(siv, errp);
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 = r->begin;
213     }
214 
215     *obj = siv->cur;
216     siv->cur++;
217     return;
218 
219 error:
220     error_set(errp, QERR_INVALID_PARAMETER_VALUE, name,
221               "an int64 value or range");
222 }
223 
224 static void parse_type_size(Visitor *v, uint64_t *obj, const char *name,
225                             Error **errp)
226 {
227     StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
228     Error *err = NULL;
229     uint64_t val;
230 
231     if (siv->string) {
232         parse_option_size(name, siv->string, &val, &err);
233     } else {
234         error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
235                   "size");
236         return;
237     }
238     if (err) {
239         error_propagate(errp, err);
240         return;
241     }
242 
243     *obj = val;
244 }
245 
246 static void parse_type_bool(Visitor *v, bool *obj, const char *name,
247                             Error **errp)
248 {
249     StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
250 
251     if (siv->string) {
252         if (!strcasecmp(siv->string, "on") ||
253             !strcasecmp(siv->string, "yes") ||
254             !strcasecmp(siv->string, "true")) {
255             *obj = true;
256             return;
257         }
258         if (!strcasecmp(siv->string, "off") ||
259             !strcasecmp(siv->string, "no") ||
260             !strcasecmp(siv->string, "false")) {
261             *obj = false;
262             return;
263         }
264     }
265 
266     error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
267               "boolean");
268 }
269 
270 static void parse_type_str(Visitor *v, char **obj, const char *name,
271                            Error **errp)
272 {
273     StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
274     if (siv->string) {
275         *obj = g_strdup(siv->string);
276     } else {
277         error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
278                   "string");
279     }
280 }
281 
282 static void parse_type_number(Visitor *v, double *obj, const char *name,
283                               Error **errp)
284 {
285     StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v);
286     char *endp = (char *) siv->string;
287     double val;
288 
289     errno = 0;
290     if (siv->string) {
291         val = strtod(siv->string, &endp);
292     }
293     if (!siv->string || errno || endp == siv->string || *endp) {
294         error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
295                   "number");
296         return;
297     }
298 
299     *obj = val;
300 }
301 
302 static void parse_optional(Visitor *v, bool *present, const char *name,
303                            Error **errp)
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