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