diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-01-02 20:35:35 -0800 |
---|---|---|
committer | Dirk Hohndel <dirk@hohndel.org> | 2014-01-02 21:17:41 -0800 |
commit | cb53a7867486c89397f1f83eb89d19511ec215ae (patch) | |
tree | 39435bd19b8f1147c4a6cb969be609628755d490 | |
parent | 5511a0e14efdb96c7622adf11185ea741cdb5226 (diff) | |
download | subsurface-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.h | 14 | ||||
-rw-r--r-- | parse-xml.c | 95 | ||||
-rw-r--r-- | strtod.c | 130 | ||||
-rw-r--r-- | subsurface.pro | 1 |
4 files changed, 144 insertions, 96 deletions
@@ -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 \ |