int
float_validator_open(
    struct sol_flow_node *node,
    void *data,
    const struct sol_flow_node_options *options)
{
    struct float_validator_data *mdata = data;
    const struct sol_flow_node_type_test_float_validator_options *opts =
        (const struct sol_flow_node_type_test_float_validator_options *)options;
    const char *it;
    char *tail;
    double *val;

    SOL_FLOW_NODE_OPTIONS_SUB_API_CHECK(options,
        SOL_FLOW_NODE_TYPE_TEST_FLOAT_VALIDATOR_OPTIONS_API_VERSION,
        -EINVAL);
    mdata->done = false;

    if (opts->sequence == NULL || *opts->sequence == '\0') {
        SOL_ERR("Option 'sequence' is either NULL or empty.");
        return -EINVAL;
    }

    sol_vector_init(&mdata->values, sizeof(double));
    it = opts->sequence;
    do {
        val = sol_vector_append(&mdata->values);
        SOL_NULL_CHECK_GOTO(val, no_memory);

        *val = sol_util_strtod_n(it, &tail, -1, false);
        if (errno) {
            SOL_WRN("Failed do convert option 'sequence' to double %s: %d", it, errno);
            goto error;
        }
        if (it == tail) {
            SOL_WRN("Failed to convert option 'sequence' to double %s", it);
            errno = EINVAL;
            goto error;
        }
        it = tail;
    } while (*tail != '\0');

    return 0;

no_memory:
    errno = ENOMEM;
error:
    sol_vector_clear(&mdata->values);
    return -errno;
}
Exemple #2
0
SOL_API int
sol_json_token_get_double(const struct sol_json_token *token, double *value)
{
    char *endptr;
    int r;

    /* NOTE: Using a copy to ensure trailing \0 and strtod() so we
     * properly parse numbers with large precision.
     *
     * Splitting the integer, fractional and exponent parts and doing
     * the math using double numbers will result in rounding errors
     * when parsing DBL_MAX using "%.64g" formatting.
     *
     * Since parsing it is complex (ie:
     * http://www.netlib.org/fp/dtoa.c), we take the short path to
     * call our helper around libc's strtod() that limits the amount
     * of bytes.
     */

    SOL_NULL_CHECK(token, -EINVAL);
    SOL_NULL_CHECK(value, -EINVAL);

    *value = sol_util_strtod_n(token->start, &endptr,
        sol_json_token_get_size(token), false);

    r = -errno;
    if (endptr == token->start)
        r = -EINVAL;
    else if (isinf(*value)) {
        SOL_DBG("token '%.*s' is infinite",
            (int)sol_json_token_get_size(token), token->start);
        if (*value < 0)
            *value = -DBL_MAX;
        else
            *value = DBL_MAX;
        r = -ERANGE;
    } else if (isnan(*value)) {
        SOL_DBG("token '%.*s' is not a number",
            (int)sol_json_token_get_size(token), token->start);
        *value = 0;
        r = -EINVAL;
    } else if (fpclassify(*value) == FP_SUBNORMAL) {
        r = 0;
    }

    return r;
}
static inline double
strtod_no_locale(const char *nptr, char **endptr)
{
    return sol_util_strtod_n(nptr, endptr, -1, false);
}
Exemple #4
0
static void
test_strtodn(void)
{
#ifdef HAVE_LOCALE
    char *oldloc;
    const char *comma_locales[] = {
        "pt", "pt_BR", "de", "it", "ru", NULL
    };
    const char *comma_locale;
#endif
    char dbl_max_str[256], neg_dbl_max_str[256];
    char dbl_max_str_overflow[256], neg_dbl_max_str_overflow[256];
    const struct test {
        const char *str;
        double reference;
        int expected_errno;
        bool use_locale;
        int endptr_offset;
    } *itr, tests[] = {
        { "0", 0.0, 0, false, -1 },
        { "123", 123.0, 0, false, -1 },
        { "1.0", 1.0, 0, false, -1 },
        { "123.456", 123.456, 0, false, -1 },
        { "345e+12", 345e12, 0, false, -1 },
        { "345e-12", 345e-12, 0, false, -1 },
        { "345E+12", 345e12, 0, false, -1 },
        { "345E-12", 345e-12, 0, false, -1 },
        { "-1.0", -1.0, 0, false, -1 },
        { "-123.456", -123.456, 0, false, -1 },
        { "-345e+12", -345e12, 0, false, -1 },
        { "-345e-12", -345e-12, 0, false, -1 },
        { "-345E+12", -345e12, 0, false, -1 },
        { "-345E-12", -345e-12, 0, false, -1 },
        { "-345.678e+12", -345.678e12, 0, false, -1 },
        { "-345.678e-12", -345.678e-12, 0, false, -1 },
        { "-345.678E+12", -345.678e12, 0, false, -1 },
        { "-345.678E-12", -345.678e-12, 0, false, -1 },
        { dbl_max_str, DBL_MAX, 0, false, -1 },
        { neg_dbl_max_str, -DBL_MAX, 0, false, -1 },
        { dbl_max_str_overflow, DBL_MAX, ERANGE, false, -1 },
        { neg_dbl_max_str_overflow, -DBL_MAX, ERANGE, false, -1 },
        { "x", 0, 0, false, 0 },
        { "1x", 1.0, 0, false, 1 },
        { "12,3", 12.0, 0, false, 2 },
        { "", 0, 0, false, 0 },
#ifdef HAVE_LOCALE
        /* commas as decimal separators */
        { "1,0", 1.0, 0, true, -1 },
        { "123,456", 123.456, 0, true, -1 },
        { "345e+12", 345e12, 0, true, -1 },
        { "345e-12", 345e-12, 0, true, -1 },
        { "345E+12", 345e12, 0, true, -1 },
        { "345E-12", 345e-12, 0, true, -1 },
        { "-1,0", -1.0, 0, true, -1 },
        { "-123,456", -123.456, 0, true, -1 },
        { "-345e+12", -345e12, 0, true, -1 },
        { "-345e-12", -345e-12, 0, true, -1 },
        { "-345E+12", -345e12, 0, true, -1 },
        { "-345E-12", -345e-12, 0, true, -1 },
        { "-345,678e+12", -345.678e12, 0, true, -1 },
        { "-345,678e-12", -345.678e-12, 0, true, -1 },
        { "-345,678E+12", -345.678e12, 0, true, -1 },
        { "-345,678E-12", -345.678e-12, 0, true, -1 },
        { "12.3", 12.0, 0, true, 2 },
#endif
        {}
    };

#ifdef HAVE_LOCALE
    oldloc = setlocale(LC_ALL, NULL);
    if (oldloc)
        oldloc = strdupa(oldloc);
    setlocale(LC_ALL, "C");
#endif

    snprintf(dbl_max_str, sizeof(dbl_max_str), "%.64g", DBL_MAX);
    snprintf(neg_dbl_max_str, sizeof(neg_dbl_max_str), "%.64g", -DBL_MAX);
    snprintf(dbl_max_str_overflow, sizeof(dbl_max_str_overflow), "%.64g0", DBL_MAX);
    snprintf(neg_dbl_max_str_overflow, sizeof(neg_dbl_max_str_overflow), "%.64g0", -DBL_MAX);

#ifdef HAVE_LOCALE
    {
        const char **loc;
        comma_locale = NULL;
        for (loc = comma_locales; *loc != NULL; loc++) {
            if (setlocale(LC_ALL, *loc)) {
                setlocale(LC_ALL, oldloc);
                SOL_DBG("Using locale '%s' to produce commas as "
                    "decimal separator. Ex: %0.2f", *loc, 1.23);
                comma_locale = *loc;
                break;
            }
        }
        if (!comma_locale) {
            setlocale(LC_ALL, oldloc);
            SOL_WRN("Couldn't find a locale with decimal commas");
        }
    }
#endif

    for (itr = tests; itr->str != NULL; itr++) {
        double value;
        char buf[512];
        char *endptr;
        size_t slen = strlen(itr->str);
        int endptr_offset, wanted_endptr_offset;
        int reterr;

        snprintf(buf, sizeof(buf), "%s123garbage", itr->str);

        if (comma_locale)
            setlocale(LC_ALL, comma_locale);
        else if (itr->use_locale) {
            SOL_DBG("SKIP (no comma locale): '%s'", itr->str);
            continue;
        }

        value = sol_util_strtod_n(buf, &endptr, slen, itr->use_locale);
        reterr = errno;

        endptr_offset = endptr - buf;

        if (comma_locale)
            setlocale(LC_ALL, oldloc);

        wanted_endptr_offset = itr->endptr_offset;
        if (wanted_endptr_offset < 0)
            wanted_endptr_offset = slen;

        if (itr->expected_errno == 0 && reterr == 0) {
            if (sol_util_double_eq(itr->reference, value)) {
                SOL_DBG("OK: parsed '%s' as %g (locale:%u)", itr->str, value,
                    itr->use_locale);
            } else {
                SOL_WRN("FAILED: parsed '%s' as %.64g where %.64g was expected"
                    " (difference = %g) (locale:%u)",
                    itr->str, value, itr->reference, itr->reference - value,
                    itr->use_locale);
                FAIL();
            }
        } else if (itr->expected_errno == 0 && reterr < 0) {
            SOL_WRN("FAILED: parsing '%s' failed with errno = %d (%s) (locale:%u)",
                itr->str, reterr, sol_util_strerrora(reterr), itr->use_locale);
            FAIL();
        } else if (itr->expected_errno != 0 && reterr == 0) {
            SOL_WRN("FAILED: parsing '%s' should fail with errno = %d (%s)"
                ", but got success with errno = %d (%s), value = %g (locale:%u)",
                itr->str,
                itr->expected_errno, sol_util_strerrora(itr->expected_errno),
                reterr, sol_util_strerrora(reterr), value, itr->use_locale);
            FAIL();
        } else if (itr->expected_errno != 0 && reterr < 0) {
            if (itr->expected_errno != reterr) {
                SOL_WRN("FAILED: parsing '%s' should fail with errno = %d (%s)"
                    ", but got errno = %d (%s), value = %g (locale:%u)",
                    itr->str,
                    itr->expected_errno, sol_util_strerrora(itr->expected_errno),
                    reterr, sol_util_strerrora(reterr), value, itr->use_locale);
                FAIL();
            } else if (!sol_util_double_eq(itr->reference, value)) {
                SOL_WRN("FAILED: parsing '%s' should result in %.64g"
                    ", but got %.64g (difference = %g) (locale:%u)",
                    itr->str, itr->reference, value, itr->reference - value,
                    itr->use_locale);
                FAIL();
            } else {
                SOL_DBG("OK: parsed '%s' as %g, setting errno = %d (%s) (locale:%u)",
                    itr->str, value, reterr, sol_util_strerrora(reterr), itr->use_locale);
            }
        }

        if (wanted_endptr_offset != endptr_offset) {
            SOL_WRN("FAILED: parsing '%s' should stop at offset %d, but got %d  (locale:%u)",
                itr->str, wanted_endptr_offset, endptr_offset, itr->use_locale);
            FAIL();
        }
    }
}