2014-01-03 04:35:35 +00:00
|
|
|
/*
|
|
|
|
* 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"
|
|
|
|
|
2014-01-08 06:51:22 +00:00
|
|
|
double strtod_flags(const char *str, const char **ptr, unsigned int flags)
|
2014-01-03 04:35:35 +00:00
|
|
|
{
|
2014-01-08 06:51:22 +00:00
|
|
|
char c;
|
|
|
|
const char *p = str, *ep;
|
2014-01-03 04:35:35 +00:00
|
|
|
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;
|
|
|
|
}
|