[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