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 "qapi/qmp/qnull.h" 20 #include "qemu/option.h" 21 #include "qemu/queue.h" 22 #include "qemu/range.h" 23 24 25 struct StringInputVisitor 26 { 27 Visitor visitor; 28 29 GList *ranges; 30 GList *cur_range; 31 int64_t cur; 32 33 const char *string; 34 void *list; /* Only needed for sanity checking the caller */ 35 }; 36 37 static StringInputVisitor *to_siv(Visitor *v) 38 { 39 return container_of(v, StringInputVisitor, visitor); 40 } 41 42 static void free_range(void *range, void *dummy) 43 { 44 g_free(range); 45 } 46 47 static int parse_str(StringInputVisitor *siv, const char *name, Error **errp) 48 { 49 char *str = (char *) siv->string; 50 long long start, end; 51 Range *cur; 52 char *endptr; 53 54 if (siv->ranges) { 55 return 0; 56 } 57 58 if (!*str) { 59 return 0; 60 } 61 62 do { 63 errno = 0; 64 start = strtoll(str, &endptr, 0); 65 if (errno == 0 && endptr > str) { 66 if (*endptr == '\0') { 67 cur = g_malloc0(sizeof(*cur)); 68 range_set_bounds(cur, start, start); 69 siv->ranges = range_list_insert(siv->ranges, cur); 70 cur = NULL; 71 str = NULL; 72 } else if (*endptr == '-') { 73 str = endptr + 1; 74 errno = 0; 75 end = strtoll(str, &endptr, 0); 76 if (errno == 0 && endptr > str && start <= end && 77 (start > INT64_MAX - 65536 || 78 end < start + 65536)) { 79 if (*endptr == '\0') { 80 cur = g_malloc0(sizeof(*cur)); 81 range_set_bounds(cur, start, end); 82 siv->ranges = range_list_insert(siv->ranges, cur); 83 cur = NULL; 84 str = NULL; 85 } else if (*endptr == ',') { 86 str = endptr + 1; 87 cur = g_malloc0(sizeof(*cur)); 88 range_set_bounds(cur, start, end); 89 siv->ranges = range_list_insert(siv->ranges, cur); 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 range_set_bounds(cur, start, start); 101 siv->ranges = range_list_insert(siv->ranges, cur); 102 cur = NULL; 103 } else { 104 goto error; 105 } 106 } else { 107 goto error; 108 } 109 } while (str); 110 111 return 0; 112 error: 113 g_list_foreach(siv->ranges, free_range, NULL); 114 g_list_free(siv->ranges); 115 siv->ranges = NULL; 116 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", 117 "an int64 value or range"); 118 return -1; 119 } 120 121 static void 122 start_list(Visitor *v, const char *name, GenericList **list, size_t size, 123 Error **errp) 124 { 125 StringInputVisitor *siv = to_siv(v); 126 127 /* We don't support visits without a list */ 128 assert(list); 129 siv->list = list; 130 131 if (parse_str(siv, name, errp) < 0) { 132 *list = NULL; 133 return; 134 } 135 136 siv->cur_range = g_list_first(siv->ranges); 137 if (siv->cur_range) { 138 Range *r = siv->cur_range->data; 139 if (r) { 140 siv->cur = range_lob(r); 141 } 142 *list = g_malloc0(size); 143 } else { 144 *list = NULL; 145 } 146 } 147 148 static GenericList *next_list(Visitor *v, GenericList *tail, size_t size) 149 { 150 StringInputVisitor *siv = to_siv(v); 151 Range *r; 152 153 if (!siv->ranges || !siv->cur_range) { 154 return NULL; 155 } 156 157 r = siv->cur_range->data; 158 if (!r) { 159 return NULL; 160 } 161 162 if (!range_contains(r, siv->cur)) { 163 siv->cur_range = g_list_next(siv->cur_range); 164 if (!siv->cur_range) { 165 return NULL; 166 } 167 r = siv->cur_range->data; 168 if (!r) { 169 return NULL; 170 } 171 siv->cur = range_lob(r); 172 } 173 174 tail->next = g_malloc0(size); 175 return tail->next; 176 } 177 178 static void check_list(Visitor *v, Error **errp) 179 { 180 const StringInputVisitor *siv = to_siv(v); 181 Range *r; 182 GList *cur_range; 183 184 if (!siv->ranges || !siv->cur_range) { 185 return; 186 } 187 188 r = siv->cur_range->data; 189 if (!r) { 190 return; 191 } 192 193 if (!range_contains(r, siv->cur)) { 194 cur_range = g_list_next(siv->cur_range); 195 if (!cur_range) { 196 return; 197 } 198 r = cur_range->data; 199 if (!r) { 200 return; 201 } 202 } 203 204 error_setg(errp, "Range contains too many values"); 205 } 206 207 static void end_list(Visitor *v, void **obj) 208 { 209 StringInputVisitor *siv = to_siv(v); 210 211 assert(siv->list == obj); 212 } 213 214 static void parse_type_int64(Visitor *v, const char *name, int64_t *obj, 215 Error **errp) 216 { 217 StringInputVisitor *siv = to_siv(v); 218 219 if (parse_str(siv, name, errp) < 0) { 220 return; 221 } 222 223 if (!siv->ranges) { 224 goto error; 225 } 226 227 if (!siv->cur_range) { 228 Range *r; 229 230 siv->cur_range = g_list_first(siv->ranges); 231 if (!siv->cur_range) { 232 goto error; 233 } 234 235 r = siv->cur_range->data; 236 if (!r) { 237 goto error; 238 } 239 240 siv->cur = range_lob(r); 241 } 242 243 *obj = siv->cur; 244 siv->cur++; 245 return; 246 247 error: 248 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", 249 "an int64 value or range"); 250 } 251 252 static void parse_type_uint64(Visitor *v, const char *name, uint64_t *obj, 253 Error **errp) 254 { 255 /* FIXME: parse_type_int64 mishandles values over INT64_MAX */ 256 int64_t i; 257 Error *err = NULL; 258 parse_type_int64(v, name, &i, &err); 259 if (err) { 260 error_propagate(errp, err); 261 } else { 262 *obj = i; 263 } 264 } 265 266 static void parse_type_size(Visitor *v, const char *name, uint64_t *obj, 267 Error **errp) 268 { 269 StringInputVisitor *siv = to_siv(v); 270 Error *err = NULL; 271 uint64_t val; 272 273 parse_option_size(name, siv->string, &val, &err); 274 if (err) { 275 error_propagate(errp, err); 276 return; 277 } 278 279 *obj = val; 280 } 281 282 static void parse_type_bool(Visitor *v, const char *name, bool *obj, 283 Error **errp) 284 { 285 StringInputVisitor *siv = to_siv(v); 286 287 if (!strcasecmp(siv->string, "on") || 288 !strcasecmp(siv->string, "yes") || 289 !strcasecmp(siv->string, "true")) { 290 *obj = true; 291 return; 292 } 293 if (!strcasecmp(siv->string, "off") || 294 !strcasecmp(siv->string, "no") || 295 !strcasecmp(siv->string, "false")) { 296 *obj = false; 297 return; 298 } 299 300 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", 301 "boolean"); 302 } 303 304 static void parse_type_str(Visitor *v, const char *name, char **obj, 305 Error **errp) 306 { 307 StringInputVisitor *siv = to_siv(v); 308 309 *obj = g_strdup(siv->string); 310 } 311 312 static void parse_type_number(Visitor *v, const char *name, double *obj, 313 Error **errp) 314 { 315 StringInputVisitor *siv = to_siv(v); 316 char *endp = (char *) siv->string; 317 double val; 318 319 errno = 0; 320 val = strtod(siv->string, &endp); 321 if (errno || endp == siv->string || *endp) { 322 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", 323 "number"); 324 return; 325 } 326 327 *obj = val; 328 } 329 330 static void parse_type_null(Visitor *v, const char *name, QNull **obj, 331 Error **errp) 332 { 333 StringInputVisitor *siv = to_siv(v); 334 335 *obj = NULL; 336 337 if (!siv->string || siv->string[0]) { 338 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", 339 "null"); 340 return; 341 } 342 343 *obj = qnull(); 344 } 345 346 static void string_input_free(Visitor *v) 347 { 348 StringInputVisitor *siv = to_siv(v); 349 350 g_list_foreach(siv->ranges, free_range, NULL); 351 g_list_free(siv->ranges); 352 g_free(siv); 353 } 354 355 Visitor *string_input_visitor_new(const char *str) 356 { 357 StringInputVisitor *v; 358 359 assert(str); 360 v = g_malloc0(sizeof(*v)); 361 362 v->visitor.type = VISITOR_INPUT; 363 v->visitor.type_int64 = parse_type_int64; 364 v->visitor.type_uint64 = parse_type_uint64; 365 v->visitor.type_size = parse_type_size; 366 v->visitor.type_bool = parse_type_bool; 367 v->visitor.type_str = parse_type_str; 368 v->visitor.type_number = parse_type_number; 369 v->visitor.type_null = parse_type_null; 370 v->visitor.start_list = start_list; 371 v->visitor.next_list = next_list; 372 v->visitor.check_list = check_list; 373 v->visitor.end_list = end_list; 374 v->visitor.free = string_input_free; 375 376 v->string = str; 377 return &v->visitor; 378 } 379