unicorn/qemu/qapi/string-input-visitor.c
Eric Blake 844c136945
qapi: Swap 'name' in visit_* callbacks to match public API
As explained in the previous patches, matching argument order of
'name, &value' to JSON's "name":value makes sense. However,
while the last two patches were easy with Coccinelle, I ended up
doing this one all by hand. Now all the visitor callbacks match
the main interface.

The compiler is able to enforce that all clients match the changed
interface in visitor-impl.h, even where two pointers are being
swapped, because only one of the two pointers is const (if that
were not the case, then C's looseness on treating 'char *' like
'void *' would have made review a bit harder).

Backports commit 0b2a0d6bb2446060944061e53e87d0c7addede79 from qemu
2018-02-19 23:36:52 -05:00

346 lines
8.5 KiB
C

/*
* String parsing visitor
*
* Copyright Red Hat, Inc. 2012-2016
*
* Author: Paolo Bonzini <pbonzini@redhat.com>
*
* This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
* See the COPYING.LIB file in the top-level directory.
*
*/
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "qapi/string-input-visitor.h"
#include "qapi/visitor-impl.h"
#include "qapi/qmp/qerror.h"
#include "qemu/queue.h"
#include "qemu/range.h"
#include <stdlib.h> // strtoll
struct StringInputVisitor
{
Visitor visitor;
bool head;
GList *ranges;
GList *cur_range;
int64_t cur;
const char *string;
};
static StringInputVisitor *to_siv(Visitor *v)
{
return container_of(v, StringInputVisitor, visitor);
}
static void free_range(void *range, void *dummy)
{
g_free(range);
}
static void parse_str(StringInputVisitor *siv, Error **errp)
{
char *str = (char *) siv->string;
long long start, end;
Range *cur;
char *endptr;
if (siv->ranges) {
return;
}
do {
errno = 0;
start = strtoll(str, &endptr, 0);
if (errno == 0 && endptr > str) {
if (*endptr == '\0') {
cur = g_malloc0(sizeof(*cur));
cur->begin = start;
cur->end = start + 1;
siv->ranges = g_list_insert_sorted_merged(siv->ranges, cur,
range_compare);
cur = NULL;
str = NULL;
} else if (*endptr == '-') {
str = endptr + 1;
errno = 0;
end = strtoll(str, &endptr, 0);
if (errno == 0 && endptr > str && start <= end &&
(start > INT64_MAX - 65536 ||
end < start + 65536)) {
if (*endptr == '\0') {
cur = g_malloc0(sizeof(*cur));
cur->begin = start;
cur->end = end + 1;
siv->ranges =
g_list_insert_sorted_merged(siv->ranges,
cur,
range_compare);
cur = NULL;
str = NULL;
} else if (*endptr == ',') {
str = endptr + 1;
cur = g_malloc0(sizeof(*cur));
cur->begin = start;
cur->end = end + 1;
siv->ranges =
g_list_insert_sorted_merged(siv->ranges,
cur,
range_compare);
cur = NULL;
} else {
goto error;
}
} else {
goto error;
}
} else if (*endptr == ',') {
str = endptr + 1;
cur = g_malloc0(sizeof(*cur));
cur->begin = start;
cur->end = start + 1;
siv->ranges = g_list_insert_sorted_merged(siv->ranges,
cur,
range_compare);
cur = NULL;
} else {
goto error;
}
} else {
goto error;
}
} while (str);
return;
error:
g_list_foreach(siv->ranges, free_range, NULL);
g_list_free(siv->ranges);
siv->ranges = NULL;
}
static void
start_list(Visitor *v, const char *name, Error **errp)
{
StringInputVisitor *siv = to_siv(v);
parse_str(siv, errp);
siv->cur_range = g_list_first(siv->ranges);
if (siv->cur_range) {
Range *r = siv->cur_range->data;
if (r) {
siv->cur = r->begin;
}
}
}
static GenericList *
next_list(Visitor *v, GenericList **list)
{
StringInputVisitor *siv = to_siv(v);
GenericList **link;
Range *r;
if (!siv->ranges || !siv->cur_range) {
return NULL;
}
r = siv->cur_range->data;
if (!r) {
return NULL;
}
if ((uint64_t)siv->cur < r->begin || (uint64_t)siv->cur >= r->end) {
siv->cur_range = g_list_next(siv->cur_range);
if (!siv->cur_range) {
return NULL;
}
r = siv->cur_range->data;
if (!r) {
return NULL;
}
siv->cur = r->begin;
}
if (siv->head) {
link = list;
siv->head = false;
} else {
link = &(*list)->next;
}
*link = g_malloc0(sizeof **link);
return *link;
}
static void
end_list(Visitor *v)
{
StringInputVisitor *siv = to_siv(v);
siv->head = true;
}
static void parse_type_int64(Visitor *v, const char *name, int64_t *obj,
Error **errp)
{
StringInputVisitor *siv = to_siv(v);
if (!siv->string) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
"integer");
return;
}
parse_str(siv, errp);
if (!siv->ranges) {
goto error;
}
if (!siv->cur_range) {
Range *r;
siv->cur_range = g_list_first(siv->ranges);
if (!siv->cur_range) {
goto error;
}
r = siv->cur_range->data;
if (!r) {
goto error;
}
siv->cur = r->begin;
}
*obj = siv->cur;
siv->cur++;
return;
error:
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name,
"an int64 value or range");
}
static void parse_type_uint64(Visitor *v, const char *name, uint64_t *obj,
Error **errp)
{
/* FIXME: parse_type_int64 mishandles values over INT64_MAX */
int64_t i;
Error *err = NULL;
parse_type_int64(v, name, &i, &err);
if (err) {
error_propagate(errp, err);
} else {
*obj = i;
}
}
static void parse_type_bool(Visitor *v, const char *name, bool *obj,
Error **errp)
{
StringInputVisitor *siv = to_siv(v);
if (siv->string) {
if (!strcasecmp(siv->string, "on") ||
!strcasecmp(siv->string, "yes") ||
!strcasecmp(siv->string, "true")) {
*obj = true;
return;
}
if (!strcasecmp(siv->string, "off") ||
!strcasecmp(siv->string, "no") ||
!strcasecmp(siv->string, "false")) {
*obj = false;
return;
}
}
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
"boolean");
}
static void parse_type_str(Visitor *v, const char *name, char **obj,
Error **errp)
{
StringInputVisitor *siv = to_siv(v);
if (siv->string) {
*obj = g_strdup(siv->string);
} else {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
"string");
}
}
static void parse_type_number(Visitor *v, const char *name, double *obj,
Error **errp)
{
StringInputVisitor *siv = to_siv(v);
char *endp = (char *) siv->string;
double val;
errno = 0;
if (siv->string) {
val = strtod(siv->string, &endp);
}
if (!siv->string || errno || endp == siv->string || *endp) {
error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
"number");
return;
}
*obj = val;
}
static void parse_optional(Visitor *v, const char *name, bool *present)
{
StringInputVisitor *siv = to_siv(v);
if (!siv->string) {
*present = false;
return;
}
*present = true;
}
Visitor *string_input_get_visitor(StringInputVisitor *v)
{
return &v->visitor;
}
void string_input_visitor_cleanup(StringInputVisitor *v)
{
g_list_foreach(v->ranges, free_range, NULL);
g_list_free(v->ranges);
g_free(v);
}
StringInputVisitor *string_input_visitor_new(const char *str)
{
StringInputVisitor *v;
v = g_malloc0(sizeof(*v));
v->visitor.type_enum = input_type_enum;
v->visitor.type_int64 = parse_type_int64;
v->visitor.type_uint64 = parse_type_uint64;
v->visitor.type_size = NULL;
v->visitor.type_bool = parse_type_bool;
v->visitor.type_str = parse_type_str;
v->visitor.type_number = parse_type_number;
v->visitor.start_list = start_list;
v->visitor.next_list = next_list;
v->visitor.end_list = end_list;
v->visitor.optional = parse_optional;
v->string = str;
v->head = true;
return v;
}