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