aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Linus Torvalds <torvalds@linux-foundation.org>2014-01-02 20:35:35 -0800
committerGravatar Dirk Hohndel <dirk@hohndel.org>2014-01-02 21:17:41 -0800
commitcb53a7867486c89397f1f83eb89d19511ec215ae (patch)
tree39435bd19b8f1147c4a6cb969be609628755d490
parent5511a0e14efdb96c7622adf11185ea741cdb5226 (diff)
downloadsubsurface-cb53a7867486c89397f1f83eb89d19511ec215ae.tar.gz
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@linux-foundation.org> Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
-rw-r--r--dive.h14
-rw-r--r--parse-xml.c95
-rw-r--r--strtod.c130
-rw-r--r--subsurface.pro1
4 files changed, 144 insertions, 96 deletions
diff --git a/dive.h b/dive.h
index dab4409da..a0c22935f 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 cb8312008..59e04efb1 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 000000000..4643cfe9d
--- /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 ad5af3061..3d07eced7 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 \