[PATCH 1/2] Make our 'ascii_strtod()' helper more generic
Dirk Hohndel
dirk at hohndel.org
Thu Jan 2 21:07:29 UTC 2014
This patch once again proves that it is ALWAYS worth reading submissions
from Linus.
Thanks for this one :-)
/D
On Thu, 2014-01-02 at 20:56 -0800, Linus Torvalds wrote:
> From: Linus Torvalds <torvalds at linux-foundation.org>
> Date: Thu, 2 Jan 2014 20:35:35 -0800
> Subject: [PATCH 1/2] Make our 'ascii_strtod()' helper more generic
>
> We'll want to do sane parsing of strings, but the C library makes it
> hard to handle user input sanely and the Qt toDouble() function
> interface was designed by a retarded chipmunk.
>
> So just extend our existing hacky "ascii_strtod()" to allow a more
> generic interface.
>
> Signed-off-by: Linus Torvalds <torvalds at linux-foundation.org>
> ---
>
> Preparatory patch. Shouldn't change anything visible.
>
> dive.h | 14 ++++++-
> parse-xml.c | 95 -----------------------------------------
> strtod.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> subsurface.pro | 1 +
> 4 files changed, 144 insertions(+), 96 deletions(-)
> create mode 100644 strtod.c
>
> diff --git a/dive.h b/dive.h
> index dab4409da7d4..a0c22935ff08 100644
> --- a/dive.h
> +++ b/dive.h
> @@ -618,7 +618,6 @@ struct dive *find_dive_n_near(timestamp_t when, int n, timestamp_t offset);
> /* Check if two dive computer entries are the exact same dive (-1=no/0=maybe/1=yes) */
> extern int match_one_dc(struct divecomputer *a, struct divecomputer *b);
>
> -extern double ascii_strtod(char *, char **);
> extern void parse_xml_init(void);
> extern void parse_xml_buffer(const char *url, const char *buf, int size, struct dive_table *table, const char **params, char **error);
> extern void parse_xml_exit(void);
> @@ -783,6 +782,19 @@ extern bool weightsystems_equal(weightsystem_t *ws1, weightsystem_t *ws2);
> extern void remove_cylinder(struct dive *dive, int idx);
> extern void remove_weightsystem(struct dive *dive, int idx);
>
> +/*
> + * String handling.
> + */
> +#define STRTOD_NO_SIGN 0x01
> +#define STRTOD_NO_DOT 0x02
> +#define STRTOD_NO_COMMA 0x04
> +#define STRTOD_NO_EXPONENT 0x08
> +extern double strtod_flags(char *str, char **ptr, unsigned int flags);
> +
> +#define STRTOD_ASCII (STRTOD_NO_COMMA)
> +
> +#define ascii_strtod(str,ptr) strtod_flags(str,ptr,STRTOD_ASCII)
> +
> #ifdef __cplusplus
> }
> #endif
> diff --git a/parse-xml.c b/parse-xml.c
> index cb8312008c10..59e04efb1382 100644
> --- a/parse-xml.c
> +++ b/parse-xml.c
> @@ -263,101 +263,6 @@ enum number_type {
> FLOAT
> };
>
> -double ascii_strtod(char *str, char **ptr)
> -{
> - char *p = str, c, *ep;
> - double val = 0.0;
> - double decimal = 1.0;
> - int sign = 0, esign = 0;
> - int numbers = 0, dot = 0;
> -
> - /* skip spaces */
> - while (isspace(c = *p++))
> - /* */;
> -
> - /* optional sign */
> - switch (c) {
> - case '-':
> - sign = 1;
> - /* fallthrough */
> - case '+':
> - c = *p++;
> - }
> -
> - /* Mantissa */
> - for (;;c = *p++) {
> - if (c == '.') {
> - if (dot)
> - goto done;
> - dot = 1;
> - continue;
> - }
> - if (c >= '0' && c <= '9') {
> - numbers++;
> - if (dot) {
> - decimal /= 10;
> - val += (c - '0') * decimal;
> - } else {
> - val = (val * 10) + (c - '0');
> - }
> - continue;
> - }
> - if (c != 'e' && c != 'E')
> - goto done;
> - break;
> - }
> -
> - if (!numbers)
> - goto done;
> -
> - /* Exponent */
> - ep = p;
> - c = *ep++;
> - switch (c) {
> - case '-':
> - esign = 1;
> - /* fallthrough */
> - case '+':
> - c = *ep++;
> - }
> -
> - if (c >= '0' && c <= '9') {
> - p = ep;
> - int exponent = c - '0';
> -
> - for (;;) {
> - c = *p++;
> - if (c < '0' || c > '9')
> - break;
> - exponent *= 10;
> - exponent += c - '0';
> - }
> -
> - /* We're not going to bother playing games */
> - if (exponent > 308)
> - exponent = 308;
> -
> - while (exponent-- > 0) {
> - if (esign)
> - val /= 10;
> - else
> - val *= 10;
> - }
> - }
> -
> -done:
> - if (!numbers)
> - goto no_conversion;
> - if (ptr)
> - *ptr = p-1;
> - return sign ? -val : val;
> -
> -no_conversion:
> - if (ptr)
> - *ptr = str;
> - return 0.0;
> -}
> -
> static enum number_type parse_float(char *buffer, double *res, char **endp)
> {
> double val;
> diff --git a/strtod.c b/strtod.c
> new file mode 100644
> index 000000000000..4643cfe9d66c
> --- /dev/null
> +++ b/strtod.c
> @@ -0,0 +1,130 @@
> +/*
> + * Sane helper for 'strtod()'.
> + *
> + * Sad that we even need this, but the C library version has
> + * insane locale behavior, and while the Qt "doDouble()" routines
> + * are better in that regard, they don't have an end pointer
> + * (having replaced it with the completely idiotic "ok" boolean
> + * pointer instead).
> + *
> + * I wonder what drugs people are on sometimes.
> + *
> + * Right now we support the following flags to limit the
> + * parsing some ways:
> + *
> + * STRTOD_NO_SIGN - don't accept signs
> + * STRTOD_NO_DOT - no decimal dots, I'm European
> + * STRTOD_NO_COMMA - no comma, please, I'm C locale
> + * STRTOD_NO_EXPONENT - no exponent parsing, I'm human
> + *
> + * The "negative" flags are so that the common case can just
> + * use a flag value of 0, and only if you have some special
> + * requirements do you need to state those with explicit flags.
> + *
> + * So if you want the C locale kind of parsing, you'd use the
> + * STRTOD_NO_COMMA flag to disallow a decimal comma. But if you
> + * want a more relaxed "Hey, Europeans are people too, even if
> + * they have locales with commas", just pass in a zero flag.
> + */
> +#include <ctype.h>
> +#include "dive.h"
> +
> +double strtod_flags(char *str, char **ptr, unsigned int flags)
> +{
> + char *p = str, c, *ep;
> + double val = 0.0;
> + double decimal = 1.0;
> + int sign = 0, esign = 0;
> + int numbers = 0, dot = 0;
> +
> + /* skip spaces */
> + while (isspace(c = *p++))
> + /* */;
> +
> + /* optional sign */
> + if (!(flags & STRTOD_NO_SIGN)) {
> + switch (c) {
> + case '-':
> + sign = 1;
> + /* fallthrough */
> + case '+':
> + c = *p++;
> + }
> + }
> +
> + /* Mantissa */
> + for (;;c = *p++) {
> + if ((c == '.' && !(flags & STRTOD_NO_DOT)) ||
> + (c == ',' && !(flags & STRTOD_NO_COMMA))) {
> + if (dot)
> + goto done;
> + dot = 1;
> + continue;
> + }
> + if (c >= '0' && c <= '9') {
> + numbers++;
> + if (dot) {
> + decimal /= 10;
> + val += (c - '0') * decimal;
> + } else {
> + val = (val * 10) + (c - '0');
> + }
> + continue;
> + }
> + if (c != 'e' && c != 'E')
> + goto done;
> + if (flags & STRTOD_NO_EXPONENT)
> + goto done;
> + break;
> + }
> +
> + if (!numbers)
> + goto done;
> +
> + /* Exponent */
> + ep = p;
> + c = *ep++;
> + switch (c) {
> + case '-':
> + esign = 1;
> + /* fallthrough */
> + case '+':
> + c = *ep++;
> + }
> +
> + if (c >= '0' && c <= '9') {
> + p = ep;
> + int exponent = c - '0';
> +
> + for (;;) {
> + c = *p++;
> + if (c < '0' || c > '9')
> + break;
> + exponent *= 10;
> + exponent += c - '0';
> + }
> +
> + /* We're not going to bother playing games */
> + if (exponent > 308)
> + exponent = 308;
> +
> + while (exponent-- > 0) {
> + if (esign)
> + val /= 10;
> + else
> + val *= 10;
> + }
> + }
> +
> +done:
> + if (!numbers)
> + goto no_conversion;
> + if (ptr)
> + *ptr = p-1;
> + return sign ? -val : val;
> +
> +no_conversion:
> + if (ptr)
> + *ptr = str;
> + return 0.0;
> +}
> diff --git a/subsurface.pro b/subsurface.pro
> index ad5af3061aa3..3d07eced78ed 100644
> --- a/subsurface.pro
> +++ b/subsurface.pro
> @@ -98,6 +98,7 @@ SOURCES = \
> save-xml.c \
> sha1.c \
> statistics.c \
> + strtod.c \
> subsurfacestartup.c \
> time.c \
> uemis.c \
> --
> 1.8.5.1.163.gd7aced9
>
More information about the subsurface
mailing list