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