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>
This commit is contained in:
Linus Torvalds 2014-01-02 20:35:35 -08:00 committed by Dirk Hohndel
parent 5511a0e14e
commit cb53a78674
4 changed files with 144 additions and 96 deletions

14
dive.h
View file

@ -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

View file

@ -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;

130
strtod.c Normal file
View file

@ -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;
}

View file

@ -98,6 +98,7 @@ SOURCES = \
save-xml.c \
sha1.c \
statistics.c \
strtod.c \
subsurfacestartup.c \
time.c \
uemis.c \