[PATCH 1/2] Make our 'ascii_strtod()' helper more generic

Linus Torvalds torvalds at linux-foundation.org
Thu Jan 2 20:56:33 UTC 2014


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