diff --git a/hmp.c b/hmp.c index aba728f0de..83e287e0a4 100644 --- a/hmp.c +++ b/hmp.c @@ -1344,12 +1344,11 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) { const char *param = qdict_get_str(qdict, "parameter"); const char *valuestr = qdict_get_str(qdict, "value"); - int64_t valuebw = 0; + uint64_t valuebw = 0; long valueint = 0; - char *endp; Error *err = NULL; bool use_int_value = false; - int i; + int i, ret; for (i = 0; i < MIGRATION_PARAMETER__MAX; i++) { if (strcmp(param, MigrationParameter_lookup[i]) == 0) { @@ -1385,9 +1384,9 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) break; case MIGRATION_PARAMETER_MAX_BANDWIDTH: p.has_max_bandwidth = true; - valuebw = qemu_strtosz(valuestr, &endp); - if (valuebw < 0 || (size_t)valuebw != valuebw - || *endp != '\0') { + ret = qemu_strtosz_MiB(valuestr, NULL, &valuebw); + if (ret < 0 || valuebw > INT64_MAX + || (size_t)valuebw != valuebw) { error_setg(&err, "Invalid size %s", valuestr); goto cleanup; } diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index bf57e635d6..82ce8378bf 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -1267,10 +1267,11 @@ static void ivshmem_realize(PCIDevice *dev, Error **errp) if (s->sizearg == NULL) { s->legacy_size = 4 << 20; /* 4 MB default */ } else { - char *end; - int64_t size = qemu_strtosz(s->sizearg, &end); - if (size < 0 || (size_t)size != size || *end != '\0' - || !is_power_of_2(size)) { + int ret; + uint64_t size; + + ret = qemu_strtosz_MiB(s->sizearg, NULL, &size); + if (ret < 0 || (size_t)size != size || !is_power_of_2(size)) { error_setg(errp, "Invalid size %s", s->sizearg); return; } diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h index 8033929139..f0878eaafa 100644 --- a/include/qemu/cutils.h +++ b/include/qemu/cutils.h @@ -130,34 +130,19 @@ int qemu_strtol(const char *nptr, const char **endptr, int base, long *result); int qemu_strtoul(const char *nptr, const char **endptr, int base, unsigned long *result); -int qemu_strtoll(const char *nptr, const char **endptr, int base, - int64_t *result); -int qemu_strtoull(const char *nptr, const char **endptr, int base, +int qemu_strtoi64(const char *nptr, const char **endptr, int base, + int64_t *result); +int qemu_strtou64(const char *nptr, const char **endptr, int base, uint64_t *result); int parse_uint(const char *s, unsigned long long *value, char **endptr, int base); int parse_uint_full(const char *s, unsigned long long *value, int base); -/* - * qemu_strtosz() suffixes used to specify the default treatment of an - * argument passed to qemu_strtosz() without an explicit suffix. - * These should be defined using upper case characters in the range - * A-Z, as qemu_strtosz() will use qemu_toupper() on the given argument - * prior to comparison. - */ -#define QEMU_STRTOSZ_DEFSUFFIX_EB 'E' -#define QEMU_STRTOSZ_DEFSUFFIX_PB 'P' -#define QEMU_STRTOSZ_DEFSUFFIX_TB 'T' -#define QEMU_STRTOSZ_DEFSUFFIX_GB 'G' -#define QEMU_STRTOSZ_DEFSUFFIX_MB 'M' -#define QEMU_STRTOSZ_DEFSUFFIX_KB 'K' -#define QEMU_STRTOSZ_DEFSUFFIX_B 'B' -int64_t qemu_strtosz(const char *nptr, char **end); -int64_t qemu_strtosz_suffix(const char *nptr, char **end, - const char default_suffix); -int64_t qemu_strtosz_suffix_unit(const char *nptr, char **end, - const char default_suffix, int64_t unit); +int qemu_strtosz(const char *nptr, char **end, uint64_t *result); +int qemu_strtosz_MiB(const char *nptr, char **end, uint64_t *result); +int qemu_strtosz_metric(const char *nptr, char **end, uint64_t *result); + #define K_BYTE (1ULL << 10) #define M_BYTE (1ULL << 20) #define G_BYTE (1ULL << 30) diff --git a/monitor.c b/monitor.c index 5aff7aebb0..f8f4a07cfb 100644 --- a/monitor.c +++ b/monitor.c @@ -2799,7 +2799,8 @@ static QDict *monitor_parse_arguments(Monitor *mon, break; case 'o': { - int64_t val; + int ret; + uint64_t val; char *end; while (qemu_isspace(*p)) { @@ -2811,8 +2812,8 @@ static QDict *monitor_parse_arguments(Monitor *mon, break; } } - val = qemu_strtosz(p, &end); - if (val < 0) { + ret = qemu_strtosz_MiB(p, &end, &val); + if (ret < 0 || val > INT64_MAX) { monitor_printf(mon, "invalid size\n"); goto fail; } diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c index 1048bbc84e..a0a7c0e734 100644 --- a/qapi/opts-visitor.c +++ b/qapi/opts-visitor.c @@ -481,23 +481,20 @@ opts_type_size(Visitor *v, const char *name, uint64_t *obj, Error **errp) { OptsVisitor *ov = to_ov(v); const QemuOpt *opt; - int64_t val; - char *endptr; + int err; opt = lookup_scalar(ov, name, errp); if (!opt) { return; } - val = qemu_strtosz_suffix(opt->str ? opt->str : "", &endptr, - QEMU_STRTOSZ_DEFSUFFIX_B); - if (val < 0 || *endptr) { + err = qemu_strtosz(opt->str ? opt->str : "", NULL, obj); + if (err < 0) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, opt->name, - "a size value representible as a non-negative int64"); + "a size value"); return; } - *obj = val; processed(ov, name); } diff --git a/qemu-img.c b/qemu-img.c index cff22e3005..df3aefd35a 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -368,6 +368,21 @@ static int add_old_style_options(const char *fmt, QemuOpts *opts, return 0; } +static int64_t cvtnum(const char *s) +{ + int err; + uint64_t value; + + err = qemu_strtosz(s, NULL, &value); + if (err < 0) { + return err; + } + if (value > INT64_MAX) { + return -ERANGE; + } + return value; +} + static int img_create(int argc, char **argv) { int c; @@ -461,10 +476,9 @@ static int img_create(int argc, char **argv) /* Get image size, if specified */ if (optind < argc) { int64_t sval; - char *end; - sval = qemu_strtosz_suffix(argv[optind++], &end, - QEMU_STRTOSZ_DEFSUFFIX_B); - if (sval < 0 || *end) { + + sval = cvtnum(argv[optind++]); + if (sval < 0) { if (sval == -ERANGE) { error_report("Image size must be less than 8 EiB!"); } else { @@ -1864,9 +1878,9 @@ static int img_convert(int argc, char **argv) case 'S': { int64_t sval; - char *end; - sval = qemu_strtosz_suffix(optarg, &end, QEMU_STRTOSZ_DEFSUFFIX_B); - if (sval < 0 || *end) { + + sval = cvtnum(optarg); + if (sval < 0) { error_report("Invalid minimum zero buffer size for sparse output specified"); ret = -1; goto fail_getopt; @@ -3651,11 +3665,8 @@ static int img_bench(int argc, char **argv) break; case 'o': { - char *end; - errno = 0; - offset = qemu_strtosz_suffix(optarg, &end, - QEMU_STRTOSZ_DEFSUFFIX_B); - if (offset < 0|| *end) { + offset = cvtnum(optarg); + if (offset < 0) { error_report("Invalid offset specified"); return 1; } @@ -3668,10 +3679,9 @@ static int img_bench(int argc, char **argv) case 's': { int64_t sval; - char *end; - sval = qemu_strtosz_suffix(optarg, &end, QEMU_STRTOSZ_DEFSUFFIX_B); - if (sval < 0 || sval > INT_MAX || *end) { + sval = cvtnum(optarg); + if (sval < 0 || sval > INT_MAX) { error_report("Invalid buffer size specified"); return 1; } @@ -3682,10 +3692,9 @@ static int img_bench(int argc, char **argv) case 'S': { int64_t sval; - char *end; - sval = qemu_strtosz_suffix(optarg, &end, QEMU_STRTOSZ_DEFSUFFIX_B); - if (sval < 0 || sval > INT_MAX || *end) { + sval = cvtnum(optarg); + if (sval < 0 || sval > INT_MAX) { error_report("Invalid step size specified"); return 1; } @@ -3844,12 +3853,11 @@ static int img_dd_bs(const char *arg, struct DdIo *in, struct DdIo *out, struct DdInfo *dd) { - char *end; int64_t res; - res = qemu_strtosz_suffix(arg, &end, QEMU_STRTOSZ_DEFSUFFIX_B); + res = cvtnum(arg); - if (res <= 0 || res > INT_MAX || *end) { + if (res <= 0 || res > INT_MAX) { error_report("invalid number: '%s'", arg); return 1; } @@ -3862,11 +3870,9 @@ static int img_dd_count(const char *arg, struct DdIo *in, struct DdIo *out, struct DdInfo *dd) { - char *end; + dd->count = cvtnum(arg); - dd->count = qemu_strtosz_suffix(arg, &end, QEMU_STRTOSZ_DEFSUFFIX_B); - - if (dd->count < 0 || *end) { + if (dd->count < 0) { error_report("invalid number: '%s'", arg); return 1; } @@ -3896,11 +3902,9 @@ static int img_dd_skip(const char *arg, struct DdIo *in, struct DdIo *out, struct DdInfo *dd) { - char *end; + in->offset = cvtnum(arg); - in->offset = qemu_strtosz_suffix(arg, &end, QEMU_STRTOSZ_DEFSUFFIX_B); - - if (in->offset < 0 || *end) { + if (in->offset < 0) { error_report("invalid number: '%s'", arg); return 1; } diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index e415b03cd0..7ac1576d4c 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -137,15 +137,17 @@ static char **breakline(char *input, int *count) static int64_t cvtnum(const char *s) { - char *end; - int64_t ret; + int err; + uint64_t value; - ret = qemu_strtosz_suffix(s, &end, QEMU_STRTOSZ_DEFSUFFIX_B); - if (*end != '\0') { - /* Detritus at the end of the string */ - return -EINVAL; + err = qemu_strtosz(s, NULL, &value); + if (err < 0) { + return err; } - return ret; + if (value > INT64_MAX) { + return -ERANGE; + } + return value; } static void print_cvtnum_err(int64_t rc, const char *arg) diff --git a/qobject/qdict.c b/qobject/qdict.c index b0c5364506..291eef1a19 100644 --- a/qobject/qdict.c +++ b/qobject/qdict.c @@ -743,7 +743,7 @@ static int qdict_is_list(QDict *maybe_list, Error **errp) for (ent = qdict_first(maybe_list); ent != NULL; ent = qdict_next(maybe_list, ent)) { - if (qemu_strtoll(ent->key, NULL, 10, &val) == 0) { + if (qemu_strtoi64(ent->key, NULL, 10, &val) == 0) { if (is_list == -1) { is_list = 1; } else if (!is_list) { diff --git a/qtest.c b/qtest.c index 1446719e8d..a6858272eb 100644 --- a/qtest.c +++ b/qtest.c @@ -373,8 +373,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words) uint64_t value; g_assert(words[1] && words[2]); - g_assert(qemu_strtoull(words[1], NULL, 0, &addr) == 0); - g_assert(qemu_strtoull(words[2], NULL, 0, &value) == 0); + g_assert(qemu_strtou64(words[1], NULL, 0, &addr) == 0); + g_assert(qemu_strtou64(words[2], NULL, 0, &value) == 0); if (words[0][5] == 'b') { uint8_t data = value; @@ -402,7 +402,7 @@ static void qtest_process_command(CharBackend *chr, gchar **words) uint64_t value = UINT64_C(-1); g_assert(words[1]); - g_assert(qemu_strtoull(words[1], NULL, 0, &addr) == 0); + g_assert(qemu_strtou64(words[1], NULL, 0, &addr) == 0); if (words[0][4] == 'b') { uint8_t data; @@ -428,8 +428,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words) char *enc; g_assert(words[1] && words[2]); - g_assert(qemu_strtoull(words[1], NULL, 0, &addr) == 0); - g_assert(qemu_strtoull(words[2], NULL, 0, &len) == 0); + g_assert(qemu_strtou64(words[1], NULL, 0, &addr) == 0); + g_assert(qemu_strtou64(words[2], NULL, 0, &len) == 0); /* We'd send garbage to libqtest if len is 0 */ g_assert(len); @@ -452,8 +452,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words) gchar *b64_data; g_assert(words[1] && words[2]); - g_assert(qemu_strtoull(words[1], NULL, 0, &addr) == 0); - g_assert(qemu_strtoull(words[2], NULL, 0, &len) == 0); + g_assert(qemu_strtou64(words[1], NULL, 0, &addr) == 0); + g_assert(qemu_strtou64(words[2], NULL, 0, &len) == 0); data = g_malloc(len); cpu_physical_memory_read(addr, data, len); @@ -469,8 +469,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words) size_t data_len; g_assert(words[1] && words[2] && words[3]); - g_assert(qemu_strtoull(words[1], NULL, 0, &addr) == 0); - g_assert(qemu_strtoull(words[2], NULL, 0, &len) == 0); + g_assert(qemu_strtou64(words[1], NULL, 0, &addr) == 0); + g_assert(qemu_strtou64(words[2], NULL, 0, &len) == 0); data_len = strlen(words[3]); if (data_len < 3) { @@ -498,8 +498,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words) unsigned long pattern; g_assert(words[1] && words[2] && words[3]); - g_assert(qemu_strtoull(words[1], NULL, 0, &addr) == 0); - g_assert(qemu_strtoull(words[2], NULL, 0, &len) == 0); + g_assert(qemu_strtou64(words[1], NULL, 0, &addr) == 0); + g_assert(qemu_strtou64(words[2], NULL, 0, &len) == 0); g_assert(qemu_strtoul(words[3], NULL, 0, &pattern) == 0); if (len) { @@ -518,8 +518,8 @@ static void qtest_process_command(CharBackend *chr, gchar **words) gsize out_len; g_assert(words[1] && words[2] && words[3]); - g_assert(qemu_strtoull(words[1], NULL, 0, &addr) == 0); - g_assert(qemu_strtoull(words[2], NULL, 0, &len) == 0); + g_assert(qemu_strtou64(words[1], NULL, 0, &addr) == 0); + g_assert(qemu_strtou64(words[2], NULL, 0, &len) == 0); data_len = strlen(words[3]); if (data_len < 3) { @@ -552,9 +552,9 @@ static void qtest_process_command(CharBackend *chr, gchar **words) unsigned long nargs, nret; g_assert(qemu_strtoul(words[2], NULL, 0, &nargs) == 0); - g_assert(qemu_strtoull(words[3], NULL, 0, &args) == 0); + g_assert(qemu_strtou64(words[3], NULL, 0, &args) == 0); g_assert(qemu_strtoul(words[4], NULL, 0, &nret) == 0); - g_assert(qemu_strtoull(words[5], NULL, 0, &ret) == 0); + g_assert(qemu_strtou64(words[5], NULL, 0, &ret) == 0); res = qtest_rtas_call(words[1], nargs, args, nret, ret); qtest_send_prefix(chr); @@ -564,7 +564,7 @@ static void qtest_process_command(CharBackend *chr, gchar **words) int64_t ns; if (words[1]) { - g_assert(qemu_strtoll(words[1], NULL, 0, &ns) == 0); + g_assert(qemu_strtoi64(words[1], NULL, 0, &ns) == 0); } else { ns = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL); } @@ -576,7 +576,7 @@ static void qtest_process_command(CharBackend *chr, gchar **words) int64_t ns; g_assert(words[1]); - g_assert(qemu_strtoll(words[1], NULL, 0, &ns) == 0); + g_assert(qemu_strtoi64(words[1], NULL, 0, &ns) == 0); qtest_clock_warp(ns); qtest_send_prefix(chr); qtest_sendf(chr, "OK %"PRIi64"\n", diff --git a/target/i386/cpu.c b/target/i386/cpu.c index fd7add2521..b6f157dca3 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -2033,12 +2033,11 @@ static void x86_cpu_parse_featurestr(const char *typename, char *features, /* Special case: */ if (!strcmp(name, "tsc-freq")) { - int64_t tsc_freq; - char *err; + int ret; + uint64_t tsc_freq; - tsc_freq = qemu_strtosz_suffix_unit(val, &err, - QEMU_STRTOSZ_DEFSUFFIX_B, 1000); - if (tsc_freq < 0 || *err) { + ret = qemu_strtosz_metric(val, NULL, &tsc_freq); + if (ret < 0 || tsc_freq > INT64_MAX) { error_setg(errp, "bad numerical value %s", val); return; } diff --git a/tests/test-cutils.c b/tests/test-cutils.c index 20b0f59ba2..f64a49b7fb 100644 --- a/tests/test-cutils.c +++ b/tests/test-cutils.c @@ -262,6 +262,7 @@ static void test_qemu_strtol_empty(void) err = qemu_strtol(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); } static void test_qemu_strtol_whitespace(void) @@ -275,6 +276,7 @@ static void test_qemu_strtol_whitespace(void) err = qemu_strtol(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); } static void test_qemu_strtol_invalid(void) @@ -288,6 +290,7 @@ static void test_qemu_strtol_invalid(void) err = qemu_strtol(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); } static void test_qemu_strtol_trailing(void) @@ -520,7 +523,7 @@ static void test_qemu_strtoul_correct(void) err = qemu_strtoul(str, &endptr, 0, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 12345); + g_assert_cmpuint(res, ==, 12345); g_assert(endptr == str + 5); } @@ -548,6 +551,7 @@ static void test_qemu_strtoul_empty(void) err = qemu_strtoul(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); } static void test_qemu_strtoul_whitespace(void) @@ -561,6 +565,7 @@ static void test_qemu_strtoul_whitespace(void) err = qemu_strtoul(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); } static void test_qemu_strtoul_invalid(void) @@ -574,6 +579,7 @@ static void test_qemu_strtoul_invalid(void) err = qemu_strtoul(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); } static void test_qemu_strtoul_trailing(void) @@ -587,7 +593,7 @@ static void test_qemu_strtoul_trailing(void) err = qemu_strtoul(str, &endptr, 0, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 123); + g_assert_cmpuint(res, ==, 123); g_assert(endptr == str + 3); } @@ -602,7 +608,7 @@ static void test_qemu_strtoul_octal(void) err = qemu_strtoul(str, &endptr, 8, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0123); + g_assert_cmpuint(res, ==, 0123); g_assert(endptr == str + strlen(str)); res = 999; @@ -610,7 +616,7 @@ static void test_qemu_strtoul_octal(void) err = qemu_strtoul(str, &endptr, 0, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0123); + g_assert_cmpuint(res, ==, 0123); g_assert(endptr == str + strlen(str)); } @@ -625,7 +631,7 @@ static void test_qemu_strtoul_decimal(void) err = qemu_strtoul(str, &endptr, 10, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 123); + g_assert_cmpuint(res, ==, 123); g_assert(endptr == str + strlen(str)); str = "123"; @@ -634,7 +640,7 @@ static void test_qemu_strtoul_decimal(void) err = qemu_strtoul(str, &endptr, 0, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 123); + g_assert_cmpuint(res, ==, 123); g_assert(endptr == str + strlen(str)); } @@ -649,7 +655,7 @@ static void test_qemu_strtoul_hex(void) err = qemu_strtoul(str, &endptr, 16, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0x123); + g_assert_cmphex(res, ==, 0x123); g_assert(endptr == str + strlen(str)); str = "0x123"; @@ -658,7 +664,7 @@ static void test_qemu_strtoul_hex(void) err = qemu_strtoul(str, &endptr, 0, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0x123); + g_assert_cmphex(res, ==, 0x123); g_assert(endptr == str + strlen(str)); } @@ -673,7 +679,7 @@ static void test_qemu_strtoul_max(void) err = qemu_strtoul(str, &endptr, 0, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, ULONG_MAX); + g_assert_cmphex(res, ==, ULONG_MAX); g_assert(endptr == str + strlen(str)); g_free(str); } @@ -689,7 +695,7 @@ static void test_qemu_strtoul_overflow(void) err = qemu_strtoul(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpint(res, ==, ULONG_MAX); + g_assert_cmphex(res, ==, ULONG_MAX); g_assert(endptr == str + strlen(str)); } @@ -704,7 +710,7 @@ static void test_qemu_strtoul_underflow(void) err = qemu_strtoul(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpint(res, ==, -1ul); + g_assert_cmpuint(res, ==, -1ul); g_assert(endptr == str + strlen(str)); } @@ -719,7 +725,7 @@ static void test_qemu_strtoul_negative(void) err = qemu_strtoul(str, &endptr, 0, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, -321ul); + g_assert_cmpuint(res, ==, -321ul); g_assert(endptr == str + strlen(str)); } @@ -732,7 +738,7 @@ static void test_qemu_strtoul_full_correct(void) err = qemu_strtoul(str, NULL, 0, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 123); + g_assert_cmpuint(res, ==, 123); } static void test_qemu_strtoul_full_null(void) @@ -763,7 +769,7 @@ static void test_qemu_strtoul_full_negative(void) err = qemu_strtoul(str, NULL, 0, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, -321ul); + g_assert_cmpuint(res, ==, -321ul); } static void test_qemu_strtoul_full_trailing(void) @@ -786,11 +792,11 @@ static void test_qemu_strtoul_full_max(void) err = qemu_strtoul(str, NULL, 0, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, ULONG_MAX); + g_assert_cmphex(res, ==, ULONG_MAX); g_free(str); } -static void test_qemu_strtoll_correct(void) +static void test_qemu_strtoi64_correct(void) { const char *str = "12345 foo"; char f = 'X'; @@ -798,27 +804,27 @@ static void test_qemu_strtoll_correct(void) int64_t res = 999; int err; - err = qemu_strtoll(str, &endptr, 0, &res); + err = qemu_strtoi64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 12345); g_assert(endptr == str + 5); } -static void test_qemu_strtoll_null(void) +static void test_qemu_strtoi64_null(void) { char f = 'X'; const char *endptr = &f; int64_t res = 999; int err; - err = qemu_strtoll(NULL, &endptr, 0, &res); + err = qemu_strtoi64(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert(endptr == NULL); } -static void test_qemu_strtoll_empty(void) +static void test_qemu_strtoi64_empty(void) { const char *str = ""; char f = 'X'; @@ -826,12 +832,13 @@ static void test_qemu_strtoll_empty(void) int64_t res = 999; int err; - err = qemu_strtoll(str, &endptr, 0, &res); + err = qemu_strtoi64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); } -static void test_qemu_strtoll_whitespace(void) +static void test_qemu_strtoi64_whitespace(void) { const char *str = " \t "; char f = 'X'; @@ -839,12 +846,13 @@ static void test_qemu_strtoll_whitespace(void) int64_t res = 999; int err; - err = qemu_strtoll(str, &endptr, 0, &res); + err = qemu_strtoi64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); } -static void test_qemu_strtoll_invalid(void) +static void test_qemu_strtoi64_invalid(void) { const char *str = " xxxx \t abc"; char f = 'X'; @@ -852,12 +860,13 @@ static void test_qemu_strtoll_invalid(void) int64_t res = 999; int err; - err = qemu_strtoll(str, &endptr, 0, &res); + err = qemu_strtoi64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); } -static void test_qemu_strtoll_trailing(void) +static void test_qemu_strtoi64_trailing(void) { const char *str = "123xxx"; char f = 'X'; @@ -865,14 +874,14 @@ static void test_qemu_strtoll_trailing(void) int64_t res = 999; int err; - err = qemu_strtoll(str, &endptr, 0, &res); + err = qemu_strtoi64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); g_assert(endptr == str + 3); } -static void test_qemu_strtoll_octal(void) +static void test_qemu_strtoi64_octal(void) { const char *str = "0123"; char f = 'X'; @@ -880,7 +889,7 @@ static void test_qemu_strtoll_octal(void) int64_t res = 999; int err; - err = qemu_strtoll(str, &endptr, 8, &res); + err = qemu_strtoi64(str, &endptr, 8, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0123); @@ -888,14 +897,14 @@ static void test_qemu_strtoll_octal(void) endptr = &f; res = 999; - err = qemu_strtoll(str, &endptr, 0, &res); + err = qemu_strtoi64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0123); g_assert(endptr == str + strlen(str)); } -static void test_qemu_strtoll_decimal(void) +static void test_qemu_strtoi64_decimal(void) { const char *str = "0123"; char f = 'X'; @@ -903,7 +912,7 @@ static void test_qemu_strtoll_decimal(void) int64_t res = 999; int err; - err = qemu_strtoll(str, &endptr, 10, &res); + err = qemu_strtoi64(str, &endptr, 10, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); @@ -912,14 +921,14 @@ static void test_qemu_strtoll_decimal(void) str = "123"; endptr = &f; res = 999; - err = qemu_strtoll(str, &endptr, 0, &res); + err = qemu_strtoi64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); g_assert(endptr == str + strlen(str)); } -static void test_qemu_strtoll_hex(void) +static void test_qemu_strtoi64_hex(void) { const char *str = "0123"; char f = 'X'; @@ -927,7 +936,7 @@ static void test_qemu_strtoll_hex(void) int64_t res = 999; int err; - err = qemu_strtoll(str, &endptr, 16, &res); + err = qemu_strtoi64(str, &endptr, 16, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0x123); @@ -936,14 +945,14 @@ static void test_qemu_strtoll_hex(void) str = "0x123"; endptr = &f; res = 999; - err = qemu_strtoll(str, &endptr, 0, &res); + err = qemu_strtoi64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 0x123); g_assert(endptr == str + strlen(str)); } -static void test_qemu_strtoll_max(void) +static void test_qemu_strtoi64_max(void) { char *str = g_strdup_printf("%lld", LLONG_MAX); char f = 'X'; @@ -951,7 +960,7 @@ static void test_qemu_strtoll_max(void) int64_t res = 999; int err; - err = qemu_strtoll(str, &endptr, 0, &res); + err = qemu_strtoi64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, LLONG_MAX); @@ -959,7 +968,7 @@ static void test_qemu_strtoll_max(void) g_free(str); } -static void test_qemu_strtoll_overflow(void) +static void test_qemu_strtoi64_overflow(void) { const char *str = "99999999999999999999999999999999999999999999"; char f = 'X'; @@ -967,14 +976,14 @@ static void test_qemu_strtoll_overflow(void) int64_t res = 999; int err; - err = qemu_strtoll(str, &endptr, 0, &res); + err = qemu_strtoi64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, LLONG_MAX); g_assert(endptr == str + strlen(str)); } -static void test_qemu_strtoll_underflow(void) +static void test_qemu_strtoi64_underflow(void) { const char *str = "-99999999999999999999999999999999999999999999"; char f = 'X'; @@ -982,14 +991,14 @@ static void test_qemu_strtoll_underflow(void) int64_t res = 999; int err; - err = qemu_strtoll(str, &endptr, 0, &res); + err = qemu_strtoi64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -ERANGE); g_assert_cmpint(res, ==, LLONG_MIN); g_assert(endptr == str + strlen(str)); } -static void test_qemu_strtoll_negative(void) +static void test_qemu_strtoi64_negative(void) { const char *str = " \t -321"; char f = 'X'; @@ -997,84 +1006,84 @@ static void test_qemu_strtoll_negative(void) int64_t res = 999; int err; - err = qemu_strtoll(str, &endptr, 0, &res); + err = qemu_strtoi64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, -321); g_assert(endptr == str + strlen(str)); } -static void test_qemu_strtoll_full_correct(void) +static void test_qemu_strtoi64_full_correct(void) { const char *str = "123"; int64_t res = 999; int err; - err = qemu_strtoll(str, NULL, 0, &res); + err = qemu_strtoi64(str, NULL, 0, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 123); } -static void test_qemu_strtoll_full_null(void) +static void test_qemu_strtoi64_full_null(void) { int64_t res = 999; int err; - err = qemu_strtoll(NULL, NULL, 0, &res); + err = qemu_strtoi64(NULL, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); } -static void test_qemu_strtoll_full_empty(void) +static void test_qemu_strtoi64_full_empty(void) { const char *str = ""; int64_t res = 999; int err; - err = qemu_strtoll(str, NULL, 0, &res); + err = qemu_strtoi64(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); } -static void test_qemu_strtoll_full_negative(void) +static void test_qemu_strtoi64_full_negative(void) { const char *str = " \t -321"; int64_t res = 999; int err; - err = qemu_strtoll(str, NULL, 0, &res); + err = qemu_strtoi64(str, NULL, 0, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, -321); } -static void test_qemu_strtoll_full_trailing(void) +static void test_qemu_strtoi64_full_trailing(void) { const char *str = "123xxx"; int64_t res = 999; int err; - err = qemu_strtoll(str, NULL, 0, &res); + err = qemu_strtoi64(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); } -static void test_qemu_strtoll_full_max(void) +static void test_qemu_strtoi64_full_max(void) { char *str = g_strdup_printf("%lld", LLONG_MAX); int64_t res; int err; - err = qemu_strtoll(str, NULL, 0, &res); + err = qemu_strtoi64(str, NULL, 0, &res); g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, LLONG_MAX); g_free(str); } -static void test_qemu_strtoull_correct(void) +static void test_qemu_strtou64_correct(void) { const char *str = "12345 foo"; char f = 'X'; @@ -1082,27 +1091,27 @@ static void test_qemu_strtoull_correct(void) uint64_t res = 999; int err; - err = qemu_strtoull(str, &endptr, 0, &res); + err = qemu_strtou64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 12345); + g_assert_cmpuint(res, ==, 12345); g_assert(endptr == str + 5); } -static void test_qemu_strtoull_null(void) +static void test_qemu_strtou64_null(void) { char f = 'X'; const char *endptr = &f; uint64_t res = 999; int err; - err = qemu_strtoull(NULL, &endptr, 0, &res); + err = qemu_strtou64(NULL, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert(endptr == NULL); } -static void test_qemu_strtoull_empty(void) +static void test_qemu_strtou64_empty(void) { const char *str = ""; char f = 'X'; @@ -1110,12 +1119,13 @@ static void test_qemu_strtoull_empty(void) uint64_t res = 999; int err; - err = qemu_strtoull(str, &endptr, 0, &res); + err = qemu_strtou64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); } -static void test_qemu_strtoull_whitespace(void) +static void test_qemu_strtou64_whitespace(void) { const char *str = " \t "; char f = 'X'; @@ -1123,12 +1133,13 @@ static void test_qemu_strtoull_whitespace(void) uint64_t res = 999; int err; - err = qemu_strtoull(str, &endptr, 0, &res); + err = qemu_strtou64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); } -static void test_qemu_strtoull_invalid(void) +static void test_qemu_strtou64_invalid(void) { const char *str = " xxxx \t abc"; char f = 'X'; @@ -1136,12 +1147,13 @@ static void test_qemu_strtoull_invalid(void) uint64_t res = 999; int err; - err = qemu_strtoull(str, &endptr, 0, &res); + err = qemu_strtou64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); } -static void test_qemu_strtoull_trailing(void) +static void test_qemu_strtou64_trailing(void) { const char *str = "123xxx"; char f = 'X'; @@ -1149,14 +1161,14 @@ static void test_qemu_strtoull_trailing(void) uint64_t res = 999; int err; - err = qemu_strtoull(str, &endptr, 0, &res); + err = qemu_strtou64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 123); + g_assert_cmpuint(res, ==, 123); g_assert(endptr == str + 3); } -static void test_qemu_strtoull_octal(void) +static void test_qemu_strtou64_octal(void) { const char *str = "0123"; char f = 'X'; @@ -1164,22 +1176,22 @@ static void test_qemu_strtoull_octal(void) uint64_t res = 999; int err; - err = qemu_strtoull(str, &endptr, 8, &res); + err = qemu_strtou64(str, &endptr, 8, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0123); + g_assert_cmpuint(res, ==, 0123); g_assert(endptr == str + strlen(str)); endptr = &f; res = 999; - err = qemu_strtoull(str, &endptr, 0, &res); + err = qemu_strtou64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0123); + g_assert_cmpuint(res, ==, 0123); g_assert(endptr == str + strlen(str)); } -static void test_qemu_strtoull_decimal(void) +static void test_qemu_strtou64_decimal(void) { const char *str = "0123"; char f = 'X'; @@ -1187,23 +1199,23 @@ static void test_qemu_strtoull_decimal(void) uint64_t res = 999; int err; - err = qemu_strtoull(str, &endptr, 10, &res); + err = qemu_strtou64(str, &endptr, 10, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 123); + g_assert_cmpuint(res, ==, 123); g_assert(endptr == str + strlen(str)); str = "123"; endptr = &f; res = 999; - err = qemu_strtoull(str, &endptr, 0, &res); + err = qemu_strtou64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 123); + g_assert_cmpuint(res, ==, 123); g_assert(endptr == str + strlen(str)); } -static void test_qemu_strtoull_hex(void) +static void test_qemu_strtou64_hex(void) { const char *str = "0123"; char f = 'X'; @@ -1211,23 +1223,23 @@ static void test_qemu_strtoull_hex(void) uint64_t res = 999; int err; - err = qemu_strtoull(str, &endptr, 16, &res); + err = qemu_strtou64(str, &endptr, 16, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0x123); + g_assert_cmphex(res, ==, 0x123); g_assert(endptr == str + strlen(str)); str = "0x123"; endptr = &f; res = 999; - err = qemu_strtoull(str, &endptr, 0, &res); + err = qemu_strtou64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 0x123); + g_assert_cmphex(res, ==, 0x123); g_assert(endptr == str + strlen(str)); } -static void test_qemu_strtoull_max(void) +static void test_qemu_strtou64_max(void) { char *str = g_strdup_printf("%llu", ULLONG_MAX); char f = 'X'; @@ -1235,15 +1247,15 @@ static void test_qemu_strtoull_max(void) uint64_t res = 999; int err; - err = qemu_strtoull(str, &endptr, 0, &res); + err = qemu_strtou64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, ULLONG_MAX); + g_assert_cmphex(res, ==, ULLONG_MAX); g_assert(endptr == str + strlen(str)); g_free(str); } -static void test_qemu_strtoull_overflow(void) +static void test_qemu_strtou64_overflow(void) { const char *str = "99999999999999999999999999999999999999999999"; char f = 'X'; @@ -1251,14 +1263,14 @@ static void test_qemu_strtoull_overflow(void) uint64_t res = 999; int err; - err = qemu_strtoull(str, &endptr, 0, &res); + err = qemu_strtou64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpint(res, ==, ULLONG_MAX); + g_assert_cmphex(res, ==, ULLONG_MAX); g_assert(endptr == str + strlen(str)); } -static void test_qemu_strtoull_underflow(void) +static void test_qemu_strtou64_underflow(void) { const char *str = "-99999999999999999999999999999999999999999999"; char f = 'X'; @@ -1266,14 +1278,14 @@ static void test_qemu_strtoull_underflow(void) uint64_t res = 999; int err; - err = qemu_strtoull(str, &endptr, 0, &res); + err = qemu_strtou64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, -ERANGE); - g_assert_cmpint(res, ==, -1); + g_assert_cmphex(res, ==, -1ull); g_assert(endptr == str + strlen(str)); } -static void test_qemu_strtoull_negative(void) +static void test_qemu_strtou64_negative(void) { const char *str = " \t -321"; char f = 'X'; @@ -1281,94 +1293,139 @@ static void test_qemu_strtoull_negative(void) uint64_t res = 999; int err; - err = qemu_strtoull(str, &endptr, 0, &res); + err = qemu_strtou64(str, &endptr, 0, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, -321); + g_assert_cmpuint(res, ==, -321ull); g_assert(endptr == str + strlen(str)); } -static void test_qemu_strtoull_full_correct(void) +static void test_qemu_strtou64_full_correct(void) { const char *str = "18446744073709551614"; uint64_t res = 999; int err; - err = qemu_strtoull(str, NULL, 0, &res); + err = qemu_strtou64(str, NULL, 0, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 18446744073709551614LLU); + g_assert_cmpuint(res, ==, 18446744073709551614ull); } -static void test_qemu_strtoull_full_null(void) +static void test_qemu_strtou64_full_null(void) { uint64_t res = 999; int err; - err = qemu_strtoull(NULL, NULL, 0, &res); + err = qemu_strtou64(NULL, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); } -static void test_qemu_strtoull_full_empty(void) +static void test_qemu_strtou64_full_empty(void) { const char *str = ""; uint64_t res = 999; int err; - err = qemu_strtoull(str, NULL, 0, &res); + err = qemu_strtou64(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); } -static void test_qemu_strtoull_full_negative(void) +static void test_qemu_strtou64_full_negative(void) { const char *str = " \t -321"; uint64_t res = 999; int err; - err = qemu_strtoull(str, NULL, 0, &res); + err = qemu_strtou64(str, NULL, 0, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, 18446744073709551295LLU); + g_assert_cmpuint(res, ==, -321ull); } -static void test_qemu_strtoull_full_trailing(void) +static void test_qemu_strtou64_full_trailing(void) { const char *str = "18446744073709551614xxxxxx"; uint64_t res = 999; int err; - err = qemu_strtoull(str, NULL, 0, &res); + err = qemu_strtou64(str, NULL, 0, &res); g_assert_cmpint(err, ==, -EINVAL); } -static void test_qemu_strtoull_full_max(void) +static void test_qemu_strtou64_full_max(void) { char *str = g_strdup_printf("%lld", ULLONG_MAX); uint64_t res = 999; int err; - err = qemu_strtoull(str, NULL, 0, &res); + err = qemu_strtou64(str, NULL, 0, &res); g_assert_cmpint(err, ==, 0); - g_assert_cmpint(res, ==, ULLONG_MAX); + g_assert_cmphex(res, ==, ULLONG_MAX); g_free(str); } static void test_qemu_strtosz_simple(void) { - const char *str = "12345M"; + const char *str; char *endptr = NULL; - int64_t res; + int err; + uint64_t res = 0xbaadf00d; - res = qemu_strtosz(str, &endptr); - g_assert_cmpint(res, ==, 12345 * M_BYTE); - g_assert(endptr == str + 6); + str = "0"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0); + g_assert(endptr == str + 1); - res = qemu_strtosz(str, NULL); - g_assert_cmpint(res, ==, 12345 * M_BYTE); + str = "12345"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 12345); + g_assert(endptr == str + 5); + + err = qemu_strtosz(str, NULL, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 12345); + + /* Note: precision is 53 bits since we're parsing with strtod() */ + + str = "9007199254740991"; /* 2^53-1 */ + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0x1fffffffffffff); + g_assert(endptr == str + 16); + + str = "9007199254740992"; /* 2^53 */ + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0x20000000000000); + g_assert(endptr == str + 16); + + str = "9007199254740993"; /* 2^53+1 */ + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0x20000000000000); /* rounded to 53 bits */ + g_assert(endptr == str + 16); + + str = "18446744073709549568"; /* 0xfffffffffffff800 (53 msbs set) */ + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0xfffffffffffff800); + g_assert(endptr == str + 20); + + str = "18446744073709550591"; /* 0xfffffffffffffbff */ + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 0xfffffffffffff800); /* rounded to 53 bits */ + g_assert(endptr == str + 20); + + /* 0x7ffffffffffffe00..0x7fffffffffffffff get rounded to + * 0x8000000000000000, thus -ERANGE; see test_qemu_strtosz_erange() */ } static void test_qemu_strtosz_units(void) @@ -1381,60 +1438,157 @@ static void test_qemu_strtosz_units(void) const char *t = "1T"; const char *p = "1P"; const char *e = "1E"; - int64_t res; + int err; + char *endptr = NULL; + uint64_t res = 0xbaadf00d; /* default is M */ - res = qemu_strtosz(none, NULL); + err = qemu_strtosz_MiB(none, &endptr, &res); + g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, M_BYTE); + g_assert(endptr == none + 1); - res = qemu_strtosz(b, NULL); + err = qemu_strtosz(b, &endptr, &res); + g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 1); + g_assert(endptr == b + 2); - res = qemu_strtosz(k, NULL); + err = qemu_strtosz(k, &endptr, &res); + g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, K_BYTE); + g_assert(endptr == k + 2); - res = qemu_strtosz(m, NULL); + err = qemu_strtosz(m, &endptr, &res); + g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, M_BYTE); + g_assert(endptr == m + 2); - res = qemu_strtosz(g, NULL); + err = qemu_strtosz(g, &endptr, &res); + g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, G_BYTE); + g_assert(endptr == g + 2); - res = qemu_strtosz(t, NULL); + err = qemu_strtosz(t, &endptr, &res); + g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, T_BYTE); + g_assert(endptr == t + 2); - res = qemu_strtosz(p, NULL); + err = qemu_strtosz(p, &endptr, &res); + g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, P_BYTE); + g_assert(endptr == p + 2); - res = qemu_strtosz(e, NULL); + err = qemu_strtosz(e, &endptr, &res); + g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, E_BYTE); + g_assert(endptr == e + 2); } static void test_qemu_strtosz_float(void) { const char *str = "12.345M"; - int64_t res; + int err; + char *endptr = NULL; + uint64_t res = 0xbaadf00d; - res = qemu_strtosz(str, NULL); + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 12.345 * M_BYTE); + g_assert(endptr == str + 7); +} + +static void test_qemu_strtosz_invalid(void) +{ + const char *str; + char *endptr = NULL; + int err; + uint64_t res = 0xbaadf00d; + + str = ""; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); + + str = " \t "; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); + + str = "crap"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); +} + +static void test_qemu_strtosz_trailing(void) +{ + const char *str; + char *endptr = NULL; + int err; + uint64_t res = 0xbaadf00d; + + str = "123xxx"; + err = qemu_strtosz_MiB(str, &endptr, &res); + g_assert_cmpint(res, ==, 123 * M_BYTE); + g_assert(endptr == str + 3); + + err = qemu_strtosz(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); + + str = "1kiB"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); + g_assert_cmpint(res, ==, 1024); + g_assert(endptr == str + 2); + + err = qemu_strtosz(str, NULL, &res); + g_assert_cmpint(err, ==, -EINVAL); } static void test_qemu_strtosz_erange(void) { - const char *str = "10E"; - int64_t res; + const char *str; + char *endptr = NULL; + int err; + uint64_t res = 0xbaadf00d; - res = qemu_strtosz(str, NULL); - g_assert_cmpint(res, ==, -ERANGE); + str = "-1"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert(endptr == str + 2); + + str = "18446744073709550592"; /* 0xfffffffffffffc00 */ + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert(endptr == str + 20); + + str = "18446744073709551615"; /* 2^64-1 */ + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert(endptr == str + 20); + + str = "18446744073709551616"; /* 2^64 */ + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert(endptr == str + 20); + + str = "20E"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -ERANGE); + g_assert(endptr == str + 3); } -static void test_qemu_strtosz_suffix_unit(void) +static void test_qemu_strtosz_metric(void) { - const char *str = "12345"; - int64_t res; + const char *str = "12345k"; + int err; + char *endptr = NULL; + uint64_t res = 0xbaadf00d; - res = qemu_strtosz_suffix_unit(str, NULL, - QEMU_STRTOSZ_DEFSUFFIX_KB, 1000); + err = qemu_strtosz_metric(str, &endptr, &res); + g_assert_cmpint(err, ==, 0); g_assert_cmpint(res, ==, 12345000); + g_assert(endptr == str + 6); } int main(int argc, char **argv) @@ -1459,21 +1613,32 @@ int main(int argc, char **argv) test_parse_uint_full_correct); /* qemu_strtol() tests */ - g_test_add_func("/cutils/qemu_strtol/correct", test_qemu_strtol_correct); - g_test_add_func("/cutils/qemu_strtol/null", test_qemu_strtol_null); - g_test_add_func("/cutils/qemu_strtol/empty", test_qemu_strtol_empty); + g_test_add_func("/cutils/qemu_strtol/correct", + test_qemu_strtol_correct); + g_test_add_func("/cutils/qemu_strtol/null", + test_qemu_strtol_null); + g_test_add_func("/cutils/qemu_strtol/empty", + test_qemu_strtol_empty); g_test_add_func("/cutils/qemu_strtol/whitespace", test_qemu_strtol_whitespace); - g_test_add_func("/cutils/qemu_strtol/invalid", test_qemu_strtol_invalid); - g_test_add_func("/cutils/qemu_strtol/trailing", test_qemu_strtol_trailing); - g_test_add_func("/cutils/qemu_strtol/octal", test_qemu_strtol_octal); - g_test_add_func("/cutils/qemu_strtol/decimal", test_qemu_strtol_decimal); - g_test_add_func("/cutils/qemu_strtol/hex", test_qemu_strtol_hex); - g_test_add_func("/cutils/qemu_strtol/max", test_qemu_strtol_max); - g_test_add_func("/cutils/qemu_strtol/overflow", test_qemu_strtol_overflow); + g_test_add_func("/cutils/qemu_strtol/invalid", + test_qemu_strtol_invalid); + g_test_add_func("/cutils/qemu_strtol/trailing", + test_qemu_strtol_trailing); + g_test_add_func("/cutils/qemu_strtol/octal", + test_qemu_strtol_octal); + g_test_add_func("/cutils/qemu_strtol/decimal", + test_qemu_strtol_decimal); + g_test_add_func("/cutils/qemu_strtol/hex", + test_qemu_strtol_hex); + g_test_add_func("/cutils/qemu_strtol/max", + test_qemu_strtol_max); + g_test_add_func("/cutils/qemu_strtol/overflow", + test_qemu_strtol_overflow); g_test_add_func("/cutils/qemu_strtol/underflow", test_qemu_strtol_underflow); - g_test_add_func("/cutils/qemu_strtol/negative", test_qemu_strtol_negative); + g_test_add_func("/cutils/qemu_strtol/negative", + test_qemu_strtol_negative); g_test_add_func("/cutils/qemu_strtol_full/correct", test_qemu_strtol_full_correct); g_test_add_func("/cutils/qemu_strtol_full/null", @@ -1488,18 +1653,26 @@ int main(int argc, char **argv) test_qemu_strtol_full_max); /* qemu_strtoul() tests */ - g_test_add_func("/cutils/qemu_strtoul/correct", test_qemu_strtoul_correct); - g_test_add_func("/cutils/qemu_strtoul/null", test_qemu_strtoul_null); - g_test_add_func("/cutils/qemu_strtoul/empty", test_qemu_strtoul_empty); + g_test_add_func("/cutils/qemu_strtoul/correct", + test_qemu_strtoul_correct); + g_test_add_func("/cutils/qemu_strtoul/null", + test_qemu_strtoul_null); + g_test_add_func("/cutils/qemu_strtoul/empty", + test_qemu_strtoul_empty); g_test_add_func("/cutils/qemu_strtoul/whitespace", test_qemu_strtoul_whitespace); - g_test_add_func("/cutils/qemu_strtoul/invalid", test_qemu_strtoul_invalid); + g_test_add_func("/cutils/qemu_strtoul/invalid", + test_qemu_strtoul_invalid); g_test_add_func("/cutils/qemu_strtoul/trailing", test_qemu_strtoul_trailing); - g_test_add_func("/cutils/qemu_strtoul/octal", test_qemu_strtoul_octal); - g_test_add_func("/cutils/qemu_strtoul/decimal", test_qemu_strtoul_decimal); - g_test_add_func("/cutils/qemu_strtoul/hex", test_qemu_strtoul_hex); - g_test_add_func("/cutils/qemu_strtoul/max", test_qemu_strtoul_max); + g_test_add_func("/cutils/qemu_strtoul/octal", + test_qemu_strtoul_octal); + g_test_add_func("/cutils/qemu_strtoul/decimal", + test_qemu_strtoul_decimal); + g_test_add_func("/cutils/qemu_strtoul/hex", + test_qemu_strtoul_hex); + g_test_add_func("/cutils/qemu_strtoul/max", + test_qemu_strtoul_max); g_test_add_func("/cutils/qemu_strtoul/overflow", test_qemu_strtoul_overflow); g_test_add_func("/cutils/qemu_strtoul/underflow", @@ -1519,73 +1692,86 @@ int main(int argc, char **argv) g_test_add_func("/cutils/qemu_strtoul_full/max", test_qemu_strtoul_full_max); - /* qemu_strtoll() tests */ - g_test_add_func("/cutils/qemu_strtoll/correct", test_qemu_strtoll_correct); - g_test_add_func("/cutils/qemu_strtoll/null", test_qemu_strtoll_null); - g_test_add_func("/cutils/qemu_strtoll/empty", test_qemu_strtoll_empty); - g_test_add_func("/cutils/qemu_strtoll/whitespace", - test_qemu_strtoll_whitespace); - g_test_add_func("/cutils/qemu_strtoll/invalid", test_qemu_strtoll_invalid); - g_test_add_func("/cutils/qemu_strtoll/trailing", - test_qemu_strtoll_trailing); - g_test_add_func("/cutils/qemu_strtoll/octal", test_qemu_strtoll_octal); - g_test_add_func("/cutils/qemu_strtoll/decimal", test_qemu_strtoll_decimal); - g_test_add_func("/cutils/qemu_strtoll/hex", test_qemu_strtoll_hex); - g_test_add_func("/cutils/qemu_strtoll/max", test_qemu_strtoll_max); - g_test_add_func("/cutils/qemu_strtoll/overflow", - test_qemu_strtoll_overflow); - g_test_add_func("/cutils/qemu_strtoll/underflow", - test_qemu_strtoll_underflow); - g_test_add_func("/cutils/qemu_strtoll/negative", - test_qemu_strtoll_negative); - g_test_add_func("/cutils/qemu_strtoll_full/correct", - test_qemu_strtoll_full_correct); - g_test_add_func("/cutils/qemu_strtoll_full/null", - test_qemu_strtoll_full_null); - g_test_add_func("/cutils/qemu_strtoll_full/empty", - test_qemu_strtoll_full_empty); - g_test_add_func("/cutils/qemu_strtoll_full/negative", - test_qemu_strtoll_full_negative); - g_test_add_func("/cutils/qemu_strtoll_full/trailing", - test_qemu_strtoll_full_trailing); - g_test_add_func("/cutils/qemu_strtoll_full/max", - test_qemu_strtoll_full_max); + /* qemu_strtoi64() tests */ + g_test_add_func("/cutils/qemu_strtoi64/correct", + test_qemu_strtoi64_correct); + g_test_add_func("/cutils/qemu_strtoi64/null", + test_qemu_strtoi64_null); + g_test_add_func("/cutils/qemu_strtoi64/empty", + test_qemu_strtoi64_empty); + g_test_add_func("/cutils/qemu_strtoi64/whitespace", + test_qemu_strtoi64_whitespace); + g_test_add_func("/cutils/qemu_strtoi64/invalid" + , + test_qemu_strtoi64_invalid); + g_test_add_func("/cutils/qemu_strtoi64/trailing", + test_qemu_strtoi64_trailing); + g_test_add_func("/cutils/qemu_strtoi64/octal", + test_qemu_strtoi64_octal); + g_test_add_func("/cutils/qemu_strtoi64/decimal", + test_qemu_strtoi64_decimal); + g_test_add_func("/cutils/qemu_strtoi64/hex", + test_qemu_strtoi64_hex); + g_test_add_func("/cutils/qemu_strtoi64/max", + test_qemu_strtoi64_max); + g_test_add_func("/cutils/qemu_strtoi64/overflow", + test_qemu_strtoi64_overflow); + g_test_add_func("/cutils/qemu_strtoi64/underflow", + test_qemu_strtoi64_underflow); + g_test_add_func("/cutils/qemu_strtoi64/negative", + test_qemu_strtoi64_negative); + g_test_add_func("/cutils/qemu_strtoi64_full/correct", + test_qemu_strtoi64_full_correct); + g_test_add_func("/cutils/qemu_strtoi64_full/null", + test_qemu_strtoi64_full_null); + g_test_add_func("/cutils/qemu_strtoi64_full/empty", + test_qemu_strtoi64_full_empty); + g_test_add_func("/cutils/qemu_strtoi64_full/negative", + test_qemu_strtoi64_full_negative); + g_test_add_func("/cutils/qemu_strtoi64_full/trailing", + test_qemu_strtoi64_full_trailing); + g_test_add_func("/cutils/qemu_strtoi64_full/max", + test_qemu_strtoi64_full_max); - /* qemu_strtoull() tests */ - g_test_add_func("/cutils/qemu_strtoull/correct", - test_qemu_strtoull_correct); - g_test_add_func("/cutils/qemu_strtoull/null", - test_qemu_strtoull_null); - g_test_add_func("/cutils/qemu_strtoull/empty", test_qemu_strtoull_empty); - g_test_add_func("/cutils/qemu_strtoull/whitespace", - test_qemu_strtoull_whitespace); - g_test_add_func("/cutils/qemu_strtoull/invalid", - test_qemu_strtoull_invalid); - g_test_add_func("/cutils/qemu_strtoull/trailing", - test_qemu_strtoull_trailing); - g_test_add_func("/cutils/qemu_strtoull/octal", test_qemu_strtoull_octal); - g_test_add_func("/cutils/qemu_strtoull/decimal", - test_qemu_strtoull_decimal); - g_test_add_func("/cutils/qemu_strtoull/hex", test_qemu_strtoull_hex); - g_test_add_func("/cutils/qemu_strtoull/max", test_qemu_strtoull_max); - g_test_add_func("/cutils/qemu_strtoull/overflow", - test_qemu_strtoull_overflow); - g_test_add_func("/cutils/qemu_strtoull/underflow", - test_qemu_strtoull_underflow); - g_test_add_func("/cutils/qemu_strtoull/negative", - test_qemu_strtoull_negative); - g_test_add_func("/cutils/qemu_strtoull_full/correct", - test_qemu_strtoull_full_correct); - g_test_add_func("/cutils/qemu_strtoull_full/null", - test_qemu_strtoull_full_null); - g_test_add_func("/cutils/qemu_strtoull_full/empty", - test_qemu_strtoull_full_empty); - g_test_add_func("/cutils/qemu_strtoull_full/negative", - test_qemu_strtoull_full_negative); - g_test_add_func("/cutils/qemu_strtoull_full/trailing", - test_qemu_strtoull_full_trailing); - g_test_add_func("/cutils/qemu_strtoull_full/max", - test_qemu_strtoull_full_max); + /* qemu_strtou64() tests */ + g_test_add_func("/cutils/qemu_strtou64/correct", + test_qemu_strtou64_correct); + g_test_add_func("/cutils/qemu_strtou64/null", + test_qemu_strtou64_null); + g_test_add_func("/cutils/qemu_strtou64/empty", + test_qemu_strtou64_empty); + g_test_add_func("/cutils/qemu_strtou64/whitespace", + test_qemu_strtou64_whitespace); + g_test_add_func("/cutils/qemu_strtou64/invalid", + test_qemu_strtou64_invalid); + g_test_add_func("/cutils/qemu_strtou64/trailing", + test_qemu_strtou64_trailing); + g_test_add_func("/cutils/qemu_strtou64/octal", + test_qemu_strtou64_octal); + g_test_add_func("/cutils/qemu_strtou64/decimal", + test_qemu_strtou64_decimal); + g_test_add_func("/cutils/qemu_strtou64/hex", + test_qemu_strtou64_hex); + g_test_add_func("/cutils/qemu_strtou64/max", + test_qemu_strtou64_max); + g_test_add_func("/cutils/qemu_strtou64/overflow", + test_qemu_strtou64_overflow); + g_test_add_func("/cutils/qemu_strtou64/underflow", + test_qemu_strtou64_underflow); + g_test_add_func("/cutils/qemu_strtou64/negative", + test_qemu_strtou64_negative); + g_test_add_func("/cutils/qemu_strtou64_full/correct", + test_qemu_strtou64_full_correct); + g_test_add_func("/cutils/qemu_strtou64_full/null", + test_qemu_strtou64_full_null); + g_test_add_func("/cutils/qemu_strtou64_full/empty", + test_qemu_strtou64_full_empty); + g_test_add_func("/cutils/qemu_strtou64_full/negative", + test_qemu_strtou64_full_negative); + g_test_add_func("/cutils/qemu_strtou64_full/trailing", + test_qemu_strtou64_full_trailing); + g_test_add_func("/cutils/qemu_strtou64_full/max", + test_qemu_strtou64_full_max); g_test_add_func("/cutils/strtosz/simple", test_qemu_strtosz_simple); @@ -1593,10 +1779,14 @@ int main(int argc, char **argv) test_qemu_strtosz_units); g_test_add_func("/cutils/strtosz/float", test_qemu_strtosz_float); + g_test_add_func("/cutils/strtosz/invalid", + test_qemu_strtosz_invalid); + g_test_add_func("/cutils/strtosz/trailing", + test_qemu_strtosz_trailing); g_test_add_func("/cutils/strtosz/erange", test_qemu_strtosz_erange); - g_test_add_func("/cutils/strtosz/suffix-unit", - test_qemu_strtosz_suffix_unit); + g_test_add_func("/cutils/strtosz/metric", + test_qemu_strtosz_metric); return g_test_run(); } diff --git a/tests/test-qemu-opts.c b/tests/test-qemu-opts.c index a505a3e059..c46ef31658 100644 --- a/tests/test-qemu-opts.c +++ b/tests/test-qemu-opts.c @@ -8,6 +8,7 @@ */ #include "qemu/osdep.h" +#include "qemu/cutils.h" #include "qapi/error.h" #include "qapi/qmp/qstring.h" #include "qemu/config-file.h" @@ -29,6 +30,9 @@ static QemuOptsList opts_list_01 = { },{ .name = "number1", .type = QEMU_OPT_NUMBER, + },{ + .name = "number2", + .type = QEMU_OPT_NUMBER, }, { /* end of list */ } }, @@ -41,15 +45,24 @@ static QemuOptsList opts_list_02 = { { .name = "str1", .type = QEMU_OPT_STRING, - },{ - .name = "bool1", - .type = QEMU_OPT_BOOL, },{ .name = "str2", .type = QEMU_OPT_STRING, + },{ + .name = "bool1", + .type = QEMU_OPT_BOOL, + },{ + .name = "bool2", + .type = QEMU_OPT_BOOL, },{ .name = "size1", .type = QEMU_OPT_SIZE, + },{ + .name = "size2", + .type = QEMU_OPT_SIZE, + },{ + .name = "size3", + .type = QEMU_OPT_SIZE, }, { /* end of list */ } }, @@ -57,6 +70,7 @@ static QemuOptsList opts_list_02 = { static QemuOptsList opts_list_03 = { .name = "opts_list_03", + .implied_opt_name = "implied", .head = QTAILQ_HEAD_INITIALIZER(opts_list_03.head), .desc = { /* no elements => accept any params */ @@ -421,6 +435,308 @@ static void test_qemu_opts_set(void) g_assert(opts == NULL); } +static int opts_count_iter(void *opaque, const char *name, const char *value, + Error **errp) +{ + (*(size_t *)opaque)++; + return 0; +} + +static size_t opts_count(QemuOpts *opts) +{ + size_t n = 0; + + qemu_opt_foreach(opts, opts_count_iter, &n, NULL); + return n; +} + +static void test_opts_parse(void) +{ + Error *err = NULL; + QemuOpts *opts; + char long_key[129]; + char *params; + + /* Nothing */ + opts = qemu_opts_parse(&opts_list_03, "", false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 0); + + /* Empty key */ + opts = qemu_opts_parse(&opts_list_03, "=val", false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 1); + g_assert_cmpstr(qemu_opt_get(opts, ""), ==, "val"); + + /* Long key */ + memset(long_key, 'a', 127); + long_key[127] = 'z'; + long_key[128] = 0; + params = g_strdup_printf("%s=v", long_key); + opts = qemu_opts_parse(&opts_list_03, params + 1, NULL, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 1); + g_assert_cmpstr(qemu_opt_get(opts, long_key + 1), ==, "v"); + + /* Overlong key gets truncated */ + opts = qemu_opts_parse(&opts_list_03, params, NULL, &error_abort); + g_assert(opts_count(opts) == 1); + long_key[127] = 0; + g_assert_cmpstr(qemu_opt_get(opts, long_key), ==, "v"); + g_free(params); + + /* Multiple keys, last one wins */ + opts = qemu_opts_parse(&opts_list_03, "a=1,b=2,,x,a=3", + false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 3); + g_assert_cmpstr(qemu_opt_get(opts, "a"), ==, "3"); + g_assert_cmpstr(qemu_opt_get(opts, "b"), ==, "2,x"); + + /* Except when it doesn't */ + opts = qemu_opts_parse(&opts_list_03, "id=foo,id=bar", + false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 0); + g_assert_cmpstr(qemu_opts_id(opts), ==, "foo"); + + /* TODO Cover low-level access to repeated keys */ + + /* Trailing comma is ignored */ + opts = qemu_opts_parse(&opts_list_03, "x=y,", false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 1); + g_assert_cmpstr(qemu_opt_get(opts, "x"), ==, "y"); + + /* Except when it isn't */ + opts = qemu_opts_parse(&opts_list_03, ",", false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 1); + g_assert_cmpstr(qemu_opt_get(opts, ""), ==, "on"); + + /* Duplicate ID */ + opts = qemu_opts_parse(&opts_list_03, "x=y,id=foo", false, &err); + error_free_or_abort(&err); + g_assert(!opts); + /* TODO Cover .merge_lists = true */ + + /* Buggy ID recognition */ + opts = qemu_opts_parse(&opts_list_03, "x=,,id=bar", false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 1); + g_assert_cmpstr(qemu_opts_id(opts), ==, "bar"); /* BUG */ + g_assert_cmpstr(qemu_opt_get(opts, "x"), ==, ",id=bar"); + + /* Anti-social ID */ + opts = qemu_opts_parse(&opts_list_01, "id=666", false, &err); + error_free_or_abort(&err); + g_assert(!opts); + + /* Implied value */ + opts = qemu_opts_parse(&opts_list_03, "an,noaus,noaus=", + false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 3); + g_assert_cmpstr(qemu_opt_get(opts, "an"), ==, "on"); + g_assert_cmpstr(qemu_opt_get(opts, "aus"), ==, "off"); + g_assert_cmpstr(qemu_opt_get(opts, "noaus"), ==, ""); + + /* Implied key */ + opts = qemu_opts_parse(&opts_list_03, "an,noaus,noaus=", true, + &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 3); + g_assert_cmpstr(qemu_opt_get(opts, "implied"), ==, "an"); + g_assert_cmpstr(qemu_opt_get(opts, "aus"), ==, "off"); + g_assert_cmpstr(qemu_opt_get(opts, "noaus"), ==, ""); + + /* Implied key with empty value */ + opts = qemu_opts_parse(&opts_list_03, ",", true, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 1); + g_assert_cmpstr(qemu_opt_get(opts, "implied"), ==, ""); + + /* Implied key with comma value */ + opts = qemu_opts_parse(&opts_list_03, ",,,a=1", true, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 2); + g_assert_cmpstr(qemu_opt_get(opts, "implied"), ==, ","); + g_assert_cmpstr(qemu_opt_get(opts, "a"), ==, "1"); + + /* Empty key is not an implied key */ + opts = qemu_opts_parse(&opts_list_03, "=val", true, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 1); + g_assert_cmpstr(qemu_opt_get(opts, ""), ==, "val"); + + /* Unknown key */ + opts = qemu_opts_parse(&opts_list_01, "nonexistent=", false, &err); + error_free_or_abort(&err); + g_assert(!opts); + + qemu_opts_reset(&opts_list_01); + qemu_opts_reset(&opts_list_03); +} + +static void test_opts_parse_bool(void) +{ + Error *err = NULL; + QemuOpts *opts; + + opts = qemu_opts_parse(&opts_list_02, "bool1=on,bool2=off", + false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 2); + g_assert(qemu_opt_get_bool(opts, "bool1", false)); + g_assert(!qemu_opt_get_bool(opts, "bool2", true)); + + opts = qemu_opts_parse(&opts_list_02, "bool1=offer", false, &err); + error_free_or_abort(&err); + g_assert(!opts); + + qemu_opts_reset(&opts_list_02); +} + +static void test_opts_parse_number(void) +{ + Error *err = NULL; + QemuOpts *opts; + + /* Lower limit zero */ + opts = qemu_opts_parse(&opts_list_01, "number1=0", false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 1); + g_assert_cmpuint(qemu_opt_get_number(opts, "number1", 1), ==, 0); + + /* Upper limit 2^64-1 */ + opts = qemu_opts_parse(&opts_list_01, + "number1=18446744073709551615,number2=-1", + false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 2); + g_assert_cmphex(qemu_opt_get_number(opts, "number1", 1), ==, UINT64_MAX); + g_assert_cmphex(qemu_opt_get_number(opts, "number2", 0), ==, UINT64_MAX); + + /* Above upper limit */ + opts = qemu_opts_parse(&opts_list_01, "number1=18446744073709551616", + false, &err); + error_free_or_abort(&err); + g_assert(!opts); + + /* Below lower limit */ + opts = qemu_opts_parse(&opts_list_01, "number1=-18446744073709551616", + false, &err); + error_free_or_abort(&err); + g_assert(!opts); + + /* Hex and octal */ + opts = qemu_opts_parse(&opts_list_01, "number1=0x2a,number2=052", + false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 2); + g_assert_cmpuint(qemu_opt_get_number(opts, "number1", 1), ==, 42); + g_assert_cmpuint(qemu_opt_get_number(opts, "number2", 0), ==, 42); + + /* Invalid */ + opts = qemu_opts_parse(&opts_list_01, "number1=", false, &err); + error_free_or_abort(&err); + g_assert(!opts); + opts = qemu_opts_parse(&opts_list_01, "number1=eins", false, &err); + error_free_or_abort(&err); + g_assert(!opts); + + /* Leading whitespace */ + opts = qemu_opts_parse(&opts_list_01, "number1= \t42", + false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 1); + g_assert_cmpuint(qemu_opt_get_number(opts, "number1", 1), ==, 42); + + /* Trailing crap */ + opts = qemu_opts_parse(&opts_list_01, "number1=3.14", false, &err); + error_free_or_abort(&err); + g_assert(!opts); + opts = qemu_opts_parse(&opts_list_01, "number1=08", false, &err); + error_free_or_abort(&err); + g_assert(!opts); + opts = qemu_opts_parse(&opts_list_01, "number1=0 ", false, &err); + error_free_or_abort(&err); + g_assert(!opts); + + qemu_opts_reset(&opts_list_01); +} + +static void test_opts_parse_size(void) +{ + Error *err = NULL; + QemuOpts *opts; + + /* Lower limit zero */ + opts = qemu_opts_parse(&opts_list_02, "size1=0", false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 1); + g_assert_cmpuint(qemu_opt_get_size(opts, "size1", 1), ==, 0); + + /* Note: precision is 53 bits since we're parsing with strtod() */ + + /* Around limit of precision: 2^53-1, 2^53, 2^54 */ + opts = qemu_opts_parse(&opts_list_02, + "size1=9007199254740991," + "size2=9007199254740992," + "size3=9007199254740993", + false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 3); + g_assert_cmphex(qemu_opt_get_size(opts, "size1", 1), + ==, 0x1fffffffffffff); + g_assert_cmphex(qemu_opt_get_size(opts, "size2", 1), + ==, 0x20000000000000); + g_assert_cmphex(qemu_opt_get_size(opts, "size3", 1), + ==, 0x20000000000000); + + /* Close to signed upper limit 0x7ffffffffffffc00 (53 msbs set) */ + opts = qemu_opts_parse(&opts_list_02, + "size1=9223372036854774784," /* 7ffffffffffffc00 */ + "size2=9223372036854775295", /* 7ffffffffffffdff */ + false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 2); + g_assert_cmphex(qemu_opt_get_size(opts, "size1", 1), + ==, 0x7ffffffffffffc00); + g_assert_cmphex(qemu_opt_get_size(opts, "size2", 1), + ==, 0x7ffffffffffffc00); + + /* Close to actual upper limit 0xfffffffffffff800 (53 msbs set) */ + opts = qemu_opts_parse(&opts_list_02, + "size1=18446744073709549568," /* fffffffffffff800 */ + "size2=18446744073709550591", /* fffffffffffffbff */ + false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 2); + g_assert_cmphex(qemu_opt_get_size(opts, "size1", 1), + ==, 0xfffffffffffff800); + g_assert_cmphex(qemu_opt_get_size(opts, "size2", 1), + ==, 0xfffffffffffff800); + + /* Beyond limits */ + opts = qemu_opts_parse(&opts_list_02, "size1=-1", false, &err); + error_free_or_abort(&err); + g_assert(!opts); + opts = qemu_opts_parse(&opts_list_02, + "size1=18446744073709550592", /* fffffffffffffc00 */ + false, &err); + error_free_or_abort(&err); + g_assert(!opts); + + /* Suffixes */ + opts = qemu_opts_parse(&opts_list_02, "size1=8b,size2=1.5k,size3=2M", + false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 3); + g_assert_cmphex(qemu_opt_get_size(opts, "size1", 0), ==, 8); + g_assert_cmphex(qemu_opt_get_size(opts, "size2", 0), ==, 1536); + g_assert_cmphex(qemu_opt_get_size(opts, "size3", 0), ==, 2 * M_BYTE); + opts = qemu_opts_parse(&opts_list_02, "size1=0.1G,size2=16777215T", + false, &error_abort); + g_assert_cmpuint(opts_count(opts), ==, 2); + g_assert_cmphex(qemu_opt_get_size(opts, "size1", 0), ==, G_BYTE / 10); + g_assert_cmphex(qemu_opt_get_size(opts, "size2", 0), + ==, 16777215 * T_BYTE); + + /* Beyond limit with suffix */ + opts = qemu_opts_parse(&opts_list_02, "size1=16777216T", + false, &err); + error_free_or_abort(&err); + g_assert(!opts); + + /* Trailing crap */ + opts = qemu_opts_parse(&opts_list_02, "size1=16E", false, &err); + error_free_or_abort(&err); + g_assert(!opts); + opts = qemu_opts_parse(&opts_list_02, "size1=16Gi", false, &err); + error_free_or_abort(&err); + g_assert(!opts); + + qemu_opts_reset(&opts_list_02); +} + int main(int argc, char *argv[]) { register_opts(); @@ -435,6 +751,10 @@ int main(int argc, char *argv[]) g_test_add_func("/qemu-opts/opt_unset", test_qemu_opt_unset); g_test_add_func("/qemu-opts/opts_reset", test_qemu_opts_reset); g_test_add_func("/qemu-opts/opts_set", test_qemu_opts_set); + g_test_add_func("/qemu-opts/opts_parse/general", test_opts_parse); + g_test_add_func("/qemu-opts/opts_parse/bool", test_opts_parse_bool); + g_test_add_func("/qemu-opts/opts_parse/number", test_opts_parse_number); + g_test_add_func("/qemu-opts/opts_parse/size", test_opts_parse_size); g_test_run(); return 0; } diff --git a/util/cutils.c b/util/cutils.c index 4fefcf3be3..50ad179dc5 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -181,19 +181,19 @@ int fcntl_setfl(int fd, int flag) static int64_t suffix_mul(char suffix, int64_t unit) { switch (qemu_toupper(suffix)) { - case QEMU_STRTOSZ_DEFSUFFIX_B: + case 'B': return 1; - case QEMU_STRTOSZ_DEFSUFFIX_KB: + case 'K': return unit; - case QEMU_STRTOSZ_DEFSUFFIX_MB: + case 'M': return unit * unit; - case QEMU_STRTOSZ_DEFSUFFIX_GB: + case 'G': return unit * unit * unit; - case QEMU_STRTOSZ_DEFSUFFIX_TB: + case 'T': return unit * unit * unit * unit; - case QEMU_STRTOSZ_DEFSUFFIX_PB: + case 'P': return unit * unit * unit * unit * unit; - case QEMU_STRTOSZ_DEFSUFFIX_EB: + case 'E': return unit * unit * unit * unit * unit * unit; } return -1; @@ -205,10 +205,11 @@ static int64_t suffix_mul(char suffix, int64_t unit) * in *end, if not NULL. Return -ERANGE on overflow, Return -EINVAL on * other error. */ -int64_t qemu_strtosz_suffix_unit(const char *nptr, char **end, - const char default_suffix, int64_t unit) +static int do_strtosz(const char *nptr, char **end, + const char default_suffix, int64_t unit, + uint64_t *result) { - int64_t retval = -EINVAL; + int retval; char *endptr; unsigned char c; int mul_required = 0; @@ -217,7 +218,8 @@ int64_t qemu_strtosz_suffix_unit(const char *nptr, char **end, errno = 0; val = strtod(nptr, &endptr); if (isnan(val) || endptr == nptr || errno != 0) { - goto fail; + retval = -EINVAL; + goto out; } fraction = modf(val, &integral); if (fraction != 0) { @@ -232,181 +234,204 @@ int64_t qemu_strtosz_suffix_unit(const char *nptr, char **end, assert(mul >= 0); } if (mul == 1 && mul_required) { - goto fail; + retval = -EINVAL; + goto out; } - if ((val * mul >= INT64_MAX) || val < 0) { + /* + * Values >= 0xfffffffffffffc00 overflow uint64_t after their trip + * through double (53 bits of precision). + */ + if ((val * mul >= 0xfffffffffffffc00) || val < 0) { retval = -ERANGE; - goto fail; + goto out; } - retval = val * mul; + *result = val * mul; + retval = 0; -fail: +out: if (end) { *end = endptr; + } else if (*endptr) { + retval = -EINVAL; } return retval; } -int64_t qemu_strtosz_suffix(const char *nptr, char **end, - const char default_suffix) +int qemu_strtosz(const char *nptr, char **end, uint64_t *result) { - return qemu_strtosz_suffix_unit(nptr, end, default_suffix, 1024); + return do_strtosz(nptr, end, 'B', 1024, result); } -int64_t qemu_strtosz(const char *nptr, char **end) +int qemu_strtosz_MiB(const char *nptr, char **end, uint64_t *result) { - return qemu_strtosz_suffix(nptr, end, QEMU_STRTOSZ_DEFSUFFIX_MB); + return do_strtosz(nptr, end, 'M', 1024, result); +} + +int qemu_strtosz_metric(const char *nptr, char **end, uint64_t *result) +{ + return do_strtosz(nptr, end, 'B', 1000, result); } /** - * Helper function for qemu_strto*l() functions. + * Helper function for error checking after strtol() and the like */ -static int check_strtox_error(const char *p, char *endptr, const char **next, - int err) +static int check_strtox_error(const char *nptr, char *ep, + const char **endptr, int libc_errno) { - /* If no conversion was performed, prefer BSD behavior over glibc - * behavior. - */ - if (err == 0 && endptr == p) { - err = EINVAL; + if (endptr) { + *endptr = ep; } - if (!next && *endptr) { + + /* Turn "no conversion" into an error */ + if (libc_errno == 0 && ep == nptr) { return -EINVAL; } - if (next) { - *next = endptr; + + /* Fail when we're expected to consume the string, but didn't */ + if (!endptr && *ep) { + return -EINVAL; } - return -err; + + return -libc_errno; } /** - * QEMU wrappers for strtol(), strtoll(), strtoul(), strotull() C functions. + * Convert string @nptr to a long integer, and store it in @result. * - * Convert ASCII string @nptr to a long integer value - * from the given @base. Parameters @nptr, @endptr, @base - * follows same semantics as strtol() C function. + * This is a wrapper around strtol() that is harder to misuse. + * Semantics of @nptr, @endptr, @base match strtol() with differences + * noted below. * - * Unlike from strtol() function, if @endptr is not NULL, this - * function will return -EINVAL whenever it cannot fully convert - * the string in @nptr with given @base to a long. This function returns - * the result of the conversion only through the @result parameter. + * @nptr may be null, and no conversion is performed then. * - * If NULL is passed in @endptr, then the whole string in @ntpr - * is a number otherwise it returns -EINVAL. + * If no conversion is performed, store @nptr in *@endptr and return + * -EINVAL. * - * RETURN VALUE - * Unlike from strtol() function, this wrapper returns either - * -EINVAL or the errno set by strtol() function (e.g -ERANGE). - * If the conversion overflows, -ERANGE is returned, and @result - * is set to the max value of the desired type - * (e.g. LONG_MAX, LLONG_MAX, ULONG_MAX, ULLONG_MAX). If the case - * of underflow, -ERANGE is returned, and @result is set to the min - * value of the desired type. For strtol(), strtoll(), @result is set to - * LONG_MIN, LLONG_MIN, respectively, and for strtoul(), strtoull() it - * is set to 0. + * If @endptr is null, and the string isn't fully converted, return + * -EINVAL. This is the case when the pointer that would be stored in + * a non-null @endptr points to a character other than '\0'. + * + * If the conversion overflows @result, store LONG_MAX in @result, + * and return -ERANGE. + * + * If the conversion underflows @result, store LONG_MIN in @result, + * and return -ERANGE. + * + * Else store the converted value in @result, and return zero. */ int qemu_strtol(const char *nptr, const char **endptr, int base, long *result) { - char *p; - int err = 0; + char *ep; + if (!nptr) { if (endptr) { *endptr = nptr; } - err = -EINVAL; - } else { - errno = 0; - *result = strtol(nptr, &p, base); - err = check_strtox_error(nptr, p, endptr, errno); + return -EINVAL; } - return err; + + errno = 0; + *result = strtol(nptr, &ep, base); + return check_strtox_error(nptr, ep, endptr, errno); } /** - * Converts ASCII string to an unsigned long integer. + * Convert string @nptr to an unsigned long, and store it in @result. * - * If string contains a negative number, value will be converted to - * the unsigned representation of the signed value, unless the original - * (nonnegated) value would overflow, in this case, it will set @result - * to ULONG_MAX, and return ERANGE. + * This is a wrapper around strtoul() that is harder to misuse. + * Semantics of @nptr, @endptr, @base match strtoul() with differences + * noted below. * - * The same behavior holds, for qemu_strtoull() but sets @result to - * ULLONG_MAX instead of ULONG_MAX. + * @nptr may be null, and no conversion is performed then. * - * See qemu_strtol() documentation for more info. + * If no conversion is performed, store @nptr in *@endptr and return + * -EINVAL. + * + * If @endptr is null, and the string isn't fully converted, return + * -EINVAL. This is the case when the pointer that would be stored in + * a non-null @endptr points to a character other than '\0'. + * + * If the conversion overflows @result, store ULONG_MAX in @result, + * and return -ERANGE. + * + * Else store the converted value in @result, and return zero. + * + * Note that a number with a leading minus sign gets converted without + * the minus sign, checked for overflow (see above), then negated (in + * @result's type). This is exactly how strtoul() works. */ int qemu_strtoul(const char *nptr, const char **endptr, int base, unsigned long *result) { - char *p; - int err = 0; + char *ep; + if (!nptr) { if (endptr) { *endptr = nptr; } - err = -EINVAL; - } else { - errno = 0; - *result = strtoul(nptr, &p, base); - /* Windows returns 1 for negative out-of-range values. */ - if (errno == ERANGE) { - *result = -1; - } - err = check_strtox_error(nptr, p, endptr, errno); + return -EINVAL; } - return err; + + errno = 0; + *result = strtoul(nptr, &ep, base); + /* Windows returns 1 for negative out-of-range values. */ + if (errno == ERANGE) { + *result = -1; + } + return check_strtox_error(nptr, ep, endptr, errno); } /** - * Converts ASCII string to a long long integer. + * Convert string @nptr to an int64_t. * - * See qemu_strtol() documentation for more info. + * Works like qemu_strtol(), except it stores INT64_MAX on overflow, + * and INT_MIN on underflow. */ -int qemu_strtoll(const char *nptr, const char **endptr, int base, +int qemu_strtoi64(const char *nptr, const char **endptr, int base, int64_t *result) { - char *p; - int err = 0; + char *ep; + if (!nptr) { if (endptr) { *endptr = nptr; } - err = -EINVAL; - } else { - errno = 0; - *result = strtoll(nptr, &p, base); - err = check_strtox_error(nptr, p, endptr, errno); + return -EINVAL; } - return err; + + errno = 0; + /* FIXME This assumes int64_t is long long */ + *result = strtoll(nptr, &ep, base); + return check_strtox_error(nptr, ep, endptr, errno); } /** - * Converts ASCII string to an unsigned long long integer. + * Convert string @nptr to an uint64_t. * - * See qemu_strtol() documentation for more info. + * Works like qemu_strtoul(), except it stores UINT64_MAX on overflow. */ -int qemu_strtoull(const char *nptr, const char **endptr, int base, +int qemu_strtou64(const char *nptr, const char **endptr, int base, uint64_t *result) { - char *p; - int err = 0; + char *ep; + if (!nptr) { if (endptr) { *endptr = nptr; } - err = -EINVAL; - } else { - errno = 0; - *result = strtoull(nptr, &p, base); - /* Windows returns 1 for negative out-of-range values. */ - if (errno == ERANGE) { - *result = -1; - } - err = check_strtox_error(nptr, p, endptr, errno); + return -EINVAL; } - return err; + + errno = 0; + /* FIXME This assumes uint64_t is unsigned long long */ + *result = strtoull(nptr, &ep, base); + /* Windows returns 1 for negative out-of-range values. */ + if (errno == ERANGE) { + *result = -1; + } + return check_strtox_error(nptr, ep, endptr, errno); } /** diff --git a/util/log.c b/util/log.c index e077340ae1..96f30dd21a 100644 --- a/util/log.c +++ b/util/log.c @@ -183,13 +183,13 @@ void qemu_set_dfilter_ranges(const char *filter_spec, Error **errp) goto out; } - if (qemu_strtoull(r, &e, 0, &r1val) + if (qemu_strtou64(r, &e, 0, &r1val) || e != range_op) { error_setg(errp, "Invalid number to the left of %.*s", (int)(r2 - range_op), range_op); goto out; } - if (qemu_strtoull(r2, NULL, 0, &r2val)) { + if (qemu_strtou64(r2, NULL, 0, &r2val)) { error_setg(errp, "Invalid number to the right of %.*s", (int)(r2 - range_op), range_op); goto out; diff --git a/util/qemu-option.c b/util/qemu-option.c index d611946333..419f2528b8 100644 --- a/util/qemu-option.c +++ b/util/qemu-option.c @@ -128,36 +128,33 @@ int get_param_value(char *buf, int buf_size, static void parse_option_bool(const char *name, const char *value, bool *ret, Error **errp) { - if (value != NULL) { - if (!strcmp(value, "on")) { - *ret = 1; - } else if (!strcmp(value, "off")) { - *ret = 0; - } else { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - name, "'on' or 'off'"); - } - } else { + if (!strcmp(value, "on")) { *ret = 1; + } else if (!strcmp(value, "off")) { + *ret = 0; + } else { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + name, "'on' or 'off'"); } } static void parse_option_number(const char *name, const char *value, uint64_t *ret, Error **errp) { - char *postfix; uint64_t number; + int err; - if (value != NULL) { - number = strtoull(value, &postfix, 0); - if (*postfix != '\0') { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name, "a number"); - return; - } - *ret = number; - } else { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name, "a number"); + err = qemu_strtou64(value, NULL, 0, &number); + if (err == -ERANGE) { + error_setg(errp, "Value '%s' is too large for parameter '%s'", + value, name); + return; } + if (err) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name, "a number"); + return; + } + *ret = number; } static const QemuOptDesc *find_desc_by_name(const QemuOptDesc *desc, @@ -177,43 +174,24 @@ static const QemuOptDesc *find_desc_by_name(const QemuOptDesc *desc, void parse_option_size(const char *name, const char *value, uint64_t *ret, Error **errp) { - char *postfix; - double sizef; + uint64_t size; + int err; - if (value != NULL) { - sizef = strtod(value, &postfix); - if (sizef < 0 || sizef > UINT64_MAX) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name, - "a non-negative number below 2^64"); - return; - } - switch (*postfix) { - case 'T': - sizef *= 1024; - /* fall through */ - case 'G': - sizef *= 1024; - /* fall through */ - case 'M': - sizef *= 1024; - /* fall through */ - case 'K': - case 'k': - sizef *= 1024; - /* fall through */ - case 'b': - case '\0': - *ret = (uint64_t) sizef; - break; - default: - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name, "a size"); - error_append_hint(errp, "You may use k, M, G or T suffixes for " - "kilobytes, megabytes, gigabytes and terabytes.\n"); - return; - } - } else { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name, "a size"); + err = qemu_strtosz(value, NULL, &size); + if (err == -ERANGE) { + error_setg(errp, "Value '%s' is too large for parameter '%s'", + value, name); + return; } + if (err) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name, + "a non-negative number below 2^64"); + error_append_hint(errp, "Optional suffix k, M, G, T, P or E means" + " kilo-, mega-, giga-, tera-, peta-\n" + "and exabytes, respectively.\n"); + return; + } + *ret = size; } bool has_help_option(const char *param) @@ -566,6 +544,7 @@ static void opt_set(QemuOpts *opts, const char *name, const char *value, } opt->desc = desc; opt->str = g_strdup(value); + assert(opt->str); qemu_opt_parse(opt, &local_err); if (local_err) { error_propagate(errp, local_err);