From: Frank Stettner Date: Thu, 2 Nov 2017 17:04:59 +0000 (+0100) Subject: drivers: Fix locale dependent string to float conversion X-Git-Url: https://sigrok.org/gitweb/?p=libsigrok.git;a=commitdiff_plain;h=4f0463a079f61ca60ac94a126a5a9cd0f40c14f3 drivers: Fix locale dependent string to float conversion Some drivers used locale dependent functions for converting strings to float/double values. These functions fail when the decimal mark is a "," in the locale settings but the string contains a ".". This fixes bug #1064. --- diff --git a/src/dmm/m2110.c b/src/dmm/m2110.c index fc092765..4f3af694 100644 --- a/src/dmm/m2110.c +++ b/src/dmm/m2110.c @@ -44,7 +44,7 @@ SR_PRIV gboolean sr_m2110_packet_valid(const uint8_t *buf) if (!strncmp((const char *)buf, "OVERRNG", 7)) return TRUE; - if (sscanf((const char *)buf, "%f", &val) == 1) + if (sr_atof_ascii((const char *)buf, &val) == SR_OK) return TRUE; else return FALSE; @@ -65,7 +65,7 @@ SR_PRIV int sr_m2110_parse(const uint8_t *buf, float *floatval, if (!strncmp((const char *)buf, "OVERRNG", 7)) *floatval = INFINITY; - else if (sscanf((const char *)buf, "%f", &val) == 1) { + else if (sr_atof_ascii((const char *)buf, &val) == SR_OK) { *floatval = val; dot_pos = strcspn((const char *)buf, "."); if (dot_pos < 7) diff --git a/src/dmm/metex14.c b/src/dmm/metex14.c index 65a3f1fe..0dd5d2ed 100644 --- a/src/dmm/metex14.c +++ b/src/dmm/metex14.c @@ -86,7 +86,7 @@ static int parse_value(const uint8_t *buf, struct metex14_info *info, return SR_OK; /* Bytes 2-8: Sign, value (up to 5 digits) and decimal point */ - sscanf((const char *)&valstr, "%f", result); + sr_atof_ascii((const char *)&valstr, result); dot_pos = strcspn(valstr, "."); if (dot_pos < cnt) diff --git a/src/hardware/motech-lps-30x/protocol.c b/src/hardware/motech-lps-30x/protocol.c index c952ed0b..ce097274 100644 --- a/src/hardware/motech-lps-30x/protocol.c +++ b/src/hardware/motech-lps-30x/protocol.c @@ -87,7 +87,7 @@ static void process_line(struct sr_dev_inst *sdi) case AQ_U2: case AQ_I1: case AQ_I2: - if (sr_atod(devc->buf, &dbl) != SR_OK) { + if (sr_atod_ascii(devc->buf, &dbl) != SR_OK) { sr_err("Failed to convert '%s' to double, errno=%d %s", devc->buf, errno, g_strerror(errno)); dbl = 0.0; diff --git a/src/libsigrok-internal.h b/src/libsigrok-internal.h index 044aa9da..9fdd81da 100644 --- a/src/libsigrok-internal.h +++ b/src/libsigrok-internal.h @@ -1010,6 +1010,7 @@ SR_PRIV int sr_atol(const char *str, long *ret); SR_PRIV int sr_atoi(const char *str, int *ret); SR_PRIV int sr_atod(const char *str, double *ret); SR_PRIV int sr_atof(const char *str, float *ret); +SR_PRIV int sr_atod_ascii(const char *str, double *ret); SR_PRIV int sr_atof_ascii(const char *str, float *ret); /*--- soft-trigger.c --------------------------------------------------------*/ diff --git a/src/scpi/scpi.c b/src/scpi/scpi.c index 84335408..511e3cd2 100644 --- a/src/scpi/scpi.c +++ b/src/scpi/scpi.c @@ -627,7 +627,7 @@ SR_PRIV int sr_scpi_get_double(struct sr_scpi_dev_inst *scpi, if (ret != SR_OK && !response) return ret; - if (sr_atod(response, scpi_response) == SR_OK) + if (sr_atod_ascii(response, scpi_response) == SR_OK) ret = SR_OK; else ret = SR_ERR_DATA; diff --git a/src/strutil.c b/src/strutil.c index 5f0deb77..30e992b8 100644 --- a/src/strutil.c +++ b/src/strutil.c @@ -169,6 +169,38 @@ SR_PRIV int sr_atof(const char *str, float *ret) return SR_OK; } +/** + * @private + * + * Convert a string representation of a numeric value to a double. The + * conversion is strict and will fail if the complete string does not represent + * a valid double. The function sets errno according to the details of the + * failure. This version ignores the locale. + * + * @param str The string representation to convert. + * @param ret Pointer to double where the result of the conversion will be stored. + * + * @retval SR_OK Conversion successful. + * @retval SR_ERR Failure. + */ +SR_PRIV int sr_atod_ascii(const char *str, double *ret) +{ + double tmp; + char *endptr = NULL; + + errno = 0; + tmp = g_ascii_strtod(str, &endptr); + + if (!endptr || *endptr || errno) { + if (!errno) + errno = EINVAL; + return SR_ERR; + } + + *ret = tmp; + return SR_OK; +} + /** * @private *