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