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-common.h" 14 #include "qapi/string-input-visitor.h" 15 #include "qapi/visitor-impl.h" 16 #include "qapi/qmp/qerror.h" 17 #include "qemu/option.h" 18 #include "qemu/queue.h" 19 #include "qemu/range.h" 20 21 22 struct StringInputVisitor 23 { 24 Visitor visitor; 25 26 bool head; 27 28 GList *ranges; 29 GList *cur_range; 30 int64_t cur; 31 32 const char *string; 33 }; 34 35 static void free_range(void *range, void *dummy) 36 { 37 g_free(range); 38 } 39 40 static void parse_str(StringInputVisitor *siv, Error **errp) 41 { 42 char *str = (char *) siv->string; 43 long long start, end; 44 Range *cur; 45 char *endptr; 46 47 if (siv->ranges) { 48 return; 49 } 50 51 do { 52 errno = 0; 53 start = strtoll(str, &endptr, 0); 54 if (errno == 0 && endptr > str) { 55 if (*endptr == '\0') { 56 cur = g_malloc0(sizeof(*cur)); 57 cur->begin = start; 58 cur->end = start + 1; 59 siv->ranges = g_list_insert_sorted_merged(siv->ranges, cur, 60 range_compare); 61 cur = NULL; 62 str = NULL; 63 } else if (*endptr == '-') { 64 str = endptr + 1; 65 errno = 0; 66 end = strtoll(str, &endptr, 0); 67 if (errno == 0 && endptr > str && start <= end && 68 (start > INT64_MAX - 65536 || 69 end < start + 65536)) { 70 if (*endptr == '\0') { 71 cur = g_malloc0(sizeof(*cur)); 72 cur->begin = start; 73 cur->end = end + 1; 74 siv->ranges = 75 g_list_insert_sorted_merged(siv->ranges, 76 cur, 77 range_compare); 78 cur = NULL; 79 str = NULL; 80 } else if (*endptr == ',') { 81 str = endptr + 1; 82 cur = g_malloc0(sizeof(*cur)); 83 cur->begin = start; 84 cur->end = end + 1; 85 siv->ranges = 86 g_list_insert_sorted_merged(siv->ranges, 87 cur, 88 range_compare); 89 cur = NULL; 90 } else { 91 goto error; 92 } 93 } else { 94 goto error; 95 } 96 } else if (*endptr == ',') { 97 str = endptr + 1; 98 cur = g_malloc0(sizeof(*cur)); 99 cur->begin = start; 100 cur->end = start + 1; 101 siv->ranges = g_list_insert_sorted_merged(siv->ranges, 102 cur, 103 range_compare); 104 cur = NULL; 105 } else { 106 goto error; 107 } 108 } else { 109 goto error; 110 } 111 } while (str); 112 113 return; 114 error: 115 g_list_foreach(siv->ranges, free_range, NULL); 116 g_list_free(siv->ranges); 117 siv->ranges = NULL; 118 } 119 120 static void 121 start_list(Visitor *v, const char *name, Error **errp) 122 { 123 StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); 124 125 parse_str(siv, errp); 126 127 siv->cur_range = g_list_first(siv->ranges); 128 if (siv->cur_range) { 129 Range *r = siv->cur_range->data; 130 if (r) { 131 siv->cur = r->begin; 132 } 133 } 134 } 135 136 static GenericList * 137 next_list(Visitor *v, GenericList **list, Error **errp) 138 { 139 StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); 140 GenericList **link; 141 Range *r; 142 143 if (!siv->ranges || !siv->cur_range) { 144 return NULL; 145 } 146 147 r = siv->cur_range->data; 148 if (!r) { 149 return NULL; 150 } 151 152 if (siv->cur < r->begin || siv->cur >= r->end) { 153 siv->cur_range = g_list_next(siv->cur_range); 154 if (!siv->cur_range) { 155 return NULL; 156 } 157 r = siv->cur_range->data; 158 if (!r) { 159 return NULL; 160 } 161 siv->cur = r->begin; 162 } 163 164 if (siv->head) { 165 link = list; 166 siv->head = false; 167 } else { 168 link = &(*list)->next; 169 } 170 171 *link = g_malloc0(sizeof **link); 172 return *link; 173 } 174 175 static void 176 end_list(Visitor *v, Error **errp) 177 { 178 StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); 179 siv->head = true; 180 } 181 182 static void parse_type_int(Visitor *v, int64_t *obj, const char *name, 183 Error **errp) 184 { 185 StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); 186 187 if (!siv->string) { 188 error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", 189 "integer"); 190 return; 191 } 192 193 parse_str(siv, errp); 194 195 if (!siv->ranges) { 196 goto error; 197 } 198 199 if (!siv->cur_range) { 200 Range *r; 201 202 siv->cur_range = g_list_first(siv->ranges); 203 if (!siv->cur_range) { 204 goto error; 205 } 206 207 r = siv->cur_range->data; 208 if (!r) { 209 goto error; 210 } 211 212 siv->cur = r->begin; 213 } 214 215 *obj = siv->cur; 216 siv->cur++; 217 return; 218 219 error: 220 error_set(errp, QERR_INVALID_PARAMETER_VALUE, name, 221 "an int64 value or range"); 222 } 223 224 static void parse_type_size(Visitor *v, uint64_t *obj, const char *name, 225 Error **errp) 226 { 227 StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); 228 Error *err = NULL; 229 uint64_t val; 230 231 if (siv->string) { 232 parse_option_size(name, siv->string, &val, &err); 233 } else { 234 error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", 235 "size"); 236 return; 237 } 238 if (err) { 239 error_propagate(errp, err); 240 return; 241 } 242 243 *obj = val; 244 } 245 246 static void parse_type_bool(Visitor *v, bool *obj, const char *name, 247 Error **errp) 248 { 249 StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); 250 251 if (siv->string) { 252 if (!strcasecmp(siv->string, "on") || 253 !strcasecmp(siv->string, "yes") || 254 !strcasecmp(siv->string, "true")) { 255 *obj = true; 256 return; 257 } 258 if (!strcasecmp(siv->string, "off") || 259 !strcasecmp(siv->string, "no") || 260 !strcasecmp(siv->string, "false")) { 261 *obj = false; 262 return; 263 } 264 } 265 266 error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", 267 "boolean"); 268 } 269 270 static void parse_type_str(Visitor *v, char **obj, const char *name, 271 Error **errp) 272 { 273 StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); 274 if (siv->string) { 275 *obj = g_strdup(siv->string); 276 } else { 277 error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", 278 "string"); 279 } 280 } 281 282 static void parse_type_number(Visitor *v, double *obj, const char *name, 283 Error **errp) 284 { 285 StringInputVisitor *siv = DO_UPCAST(StringInputVisitor, visitor, v); 286 char *endp = (char *) siv->string; 287 double val; 288 289 errno = 0; 290 if (siv->string) { 291 val = strtod(siv->string, &endp); 292 } 293 if (!siv->string || errno || endp == siv->string || *endp) { 294 error_set(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", 295 "number"); 296 return; 297 } 298 299 *obj = val; 300 } 301 302 static void parse_optional(Visitor *v, bool *present, const char *name, 303 Error **errp) 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