1 /* 2 * String parsing visitor 3 * 4 * Copyright Red Hat, Inc. 2012 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 void free_range(void *range, void *dummy) 37 { 38 g_free(range); 39 } 40 41 static void parse_str(StringInputVisitor *siv, Error **errp) 42 { 43 char *str = (char *) siv->string; 44 long long start, end; 45 Range *cur; 46 char *endptr; 47 48 if (siv->ranges) { 49 return; 50 } 51 52 do { 53 errno = 0; 54 start = strtoll(str, &endptr, 0); 55 if (errno == 0 && endptr > str) { 56 if (*endptr == '\0') { 57 cur = g_malloc0(sizeof(*cur)); 58 cur->begin = start; 59 cur->end = start + 1; 60 siv->ranges = g_list_insert_sorted_merged(siv->ranges, cur, 61 range_compare); 62 cur = NULL; 63 str = NULL; 64 } else if (*endptr == '-') { 65 str = endptr + 1; 66 errno = 0; 67 end = strtoll(str, &endptr, 0); 68 if (errno == 0 && endptr > str && start <= end && 69 (start > INT64_MAX - 65536 || 70 end < start + 65536)) { 71 if (*endptr == '\0') { 72 cur = g_malloc0(sizeof(*cur)); 73 cur->begin = start; 74 cur->end = end + 1; 75 siv->ranges = 76 g_list_insert_sorted_merged(siv->ranges, 77 cur, 78 range_compare); 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 = 87 g_list_insert_sorted_merged(siv->ranges, 88 cur, 89 range_compare); 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 cur->begin = start; 101 cur->end = start + 1; 102 siv->ranges = g_list_insert_sorted_merged(siv->ranges, 103 cur, 104 range_compare); 105 cur = NULL; 106 } else { 107 goto error; 108 } 109 } else { 110 goto error; 111 } 112 } while (str); 113 114 return; 115 error: 116 g_list_foreach(siv->ranges, free_range, NULL); 117 g_list_free(siv->ranges); 118 siv->ranges = NULL; 119 } 120 121 static void 122 start_list(Visitor *v, const char *name, Error **errp) 123 { 124 StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); 125 126 parse_str(siv, errp); 127 128 siv->cur_range = g_list_first(siv->ranges); 129 if (siv->cur_range) { 130 Range *r = siv->cur_range->data; 131 if (r) { 132 siv->cur = r->begin; 133 } 134 } 135 } 136 137 static GenericList * 138 next_list(Visitor *v, GenericList **list, Error **errp) 139 { 140 StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); 141 GenericList **link; 142 Range *r; 143 144 if (!siv->ranges || !siv->cur_range) { 145 return NULL; 146 } 147 148 r = siv->cur_range->data; 149 if (!r) { 150 return NULL; 151 } 152 153 if (siv->cur < r->begin || siv->cur >= r->end) { 154 siv->cur_range = g_list_next(siv->cur_range); 155 if (!siv->cur_range) { 156 return NULL; 157 } 158 r = siv->cur_range->data; 159 if (!r) { 160 return NULL; 161 } 162 siv->cur = r->begin; 163 } 164 165 if (siv->head) { 166 link = list; 167 siv->head = false; 168 } else { 169 link = &(*list)->next; 170 } 171 172 *link = g_malloc0(sizeof **link); 173 return *link; 174 } 175 176 static void 177 end_list(Visitor *v, Error **errp) 178 { 179 StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); 180 siv->head = true; 181 } 182 183 static void parse_type_int(Visitor *v, int64_t *obj, const char *name, 184 Error **errp) 185 { 186 StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); 187 188 if (!siv->string) { 189 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", 190 "integer"); 191 return; 192 } 193 194 parse_str(siv, errp); 195 196 if (!siv->ranges) { 197 goto error; 198 } 199 200 if (!siv->cur_range) { 201 Range *r; 202 203 siv->cur_range = g_list_first(siv->ranges); 204 if (!siv->cur_range) { 205 goto error; 206 } 207 208 r = siv->cur_range->data; 209 if (!r) { 210 goto error; 211 } 212 213 siv->cur = r->begin; 214 } 215 216 *obj = siv->cur; 217 siv->cur++; 218 return; 219 220 error: 221 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name, 222 "an int64 value or range"); 223 } 224 225 static void parse_type_size(Visitor *v, uint64_t *obj, const char *name, 226 Error **errp) 227 { 228 StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); 229 Error *err = NULL; 230 uint64_t val; 231 232 if (siv->string) { 233 parse_option_size(name, siv->string, &val, &err); 234 } else { 235 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", 236 "size"); 237 return; 238 } 239 if (err) { 240 error_propagate(errp, err); 241 return; 242 } 243 244 *obj = val; 245 } 246 247 static void parse_type_bool(Visitor *v, bool *obj, const char *name, 248 Error **errp) 249 { 250 StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); 251 252 if (siv->string) { 253 if (!strcasecmp(siv->string, "on") || 254 !strcasecmp(siv->string, "yes") || 255 !strcasecmp(siv->string, "true")) { 256 *obj = true; 257 return; 258 } 259 if (!strcasecmp(siv->string, "off") || 260 !strcasecmp(siv->string, "no") || 261 !strcasecmp(siv->string, "false")) { 262 *obj = false; 263 return; 264 } 265 } 266 267 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", 268 "boolean"); 269 } 270 271 static void parse_type_str(Visitor *v, char **obj, const char *name, 272 Error **errp) 273 { 274 StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); 275 if (siv->string) { 276 *obj = g_strdup(siv->string); 277 } else { 278 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", 279 "string"); 280 } 281 } 282 283 static void parse_type_number(Visitor *v, double *obj, const char *name, 284 Error **errp) 285 { 286 StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); 287 char *endp = (char *) siv->string; 288 double val; 289 290 errno = 0; 291 if (siv->string) { 292 val = strtod(siv->string, &endp); 293 } 294 if (!siv->string || errno || endp == siv->string || *endp) { 295 error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", 296 "number"); 297 return; 298 } 299 300 *obj = val; 301 } 302 303 static void parse_optional(Visitor *v, bool *present, const char *name) 304 { 305 StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); 306 307 if (!siv->string) { 308 *present = false; 309 return; 310 } 311 312 *present = true; 313 } 314 315 Visitor *string_input_get_visitor(StringInputVisitor *v) 316 { 317 return &v->visitor; 318 } 319 320 void string_input_visitor_cleanup(StringInputVisitor *v) 321 { 322 g_list_foreach(v->ranges, free_range, NULL); 323 g_list_free(v->ranges); 324 g_free(v); 325 } 326 327 StringInputVisitor *string_input_visitor_new(const char *str) 328 { 329 StringInputVisitor *v; 330 331 v = g_malloc0(sizeof(*v)); 332 333 v->visitor.type_enum = input_type_enum; 334 v->visitor.type_int = parse_type_int; 335 v->visitor.type_size = parse_type_size; 336 v->visitor.type_bool = parse_type_bool; 337 v->visitor.type_str = parse_type_str; 338 v->visitor.type_number = parse_type_number; 339 v->visitor.start_list = start_list; 340 v->visitor.next_list = next_list; 341 v->visitor.end_list = end_list; 342 v->visitor.optional = parse_optional; 343 344 v->string = str; 345 v->head = true; 346 return v; 347 } 348