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