core: simplify custom strtod() interface

The strtod_flags() function allowed for fine control of how to
parse strings. However, only two different modes were actually
used: ascii mode ("C" locale) and permissive mode (accept ","
and "." as decimal separator).

The former had already its own function name (ascii_strtod).
Make the latter a separatge function as well (permissive_strtod)
and remove all the flags rigmarole.

Signed-off-by: Berthold Stoeger <bstoeger@mail.tuwien.ac.at>
This commit is contained in:
Berthold Stoeger 2024-04-23 15:06:28 +08:00
parent fa178ddb9b
commit b6cabdf023
8 changed files with 57 additions and 73 deletions

View file

@ -29,7 +29,7 @@ static int cobalt_profile_sample(void *param, int, char **data, char **)
if (data[1])
state->cur_sample->depth.mm = atoi(data[1]);
if (data[2])
state->cur_sample->temperature.mkelvin = state->metric ? C_to_mkelvin(strtod_flags(data[2], NULL, 0)) : F_to_mkelvin(strtod_flags(data[2], NULL, 0));
state->cur_sample->temperature.mkelvin = state->metric ? C_to_mkelvin(permissive_strtod(data[2], NULL)) : F_to_mkelvin(permissive_strtod(data[2], NULL));
sample_end(state);
return 0;

View file

@ -284,7 +284,7 @@ static int divinglog_dive(void *param, int, char **data, char **)
utf8_string(data[4], &state->cur_dive->notes);
if (data[5])
state->cur_dive->dc.maxdepth.mm = lrint(strtod_flags(data[5], NULL, 0) * 1000);
state->cur_dive->dc.maxdepth.mm = lrint(permissive_strtod(data[5], NULL) * 1000);
if (data[6])
state->cur_dive->dc.duration.seconds = atoi(data[6]) * 60;

View file

@ -23,8 +23,8 @@ static int shearwater_cylinders(void *param, int, char **data, char **)
struct parser_state *state = (struct parser_state *)param;
cylinder_t *cyl;
int o2 = lrint(strtod_flags(data[0], NULL, 0) * 1000);
int he = lrint(strtod_flags(data[1], NULL, 0) * 1000);
int o2 = lrint(permissive_strtod(data[0], NULL) * 1000);
int he = lrint(permissive_strtod(data[1], NULL) * 1000);
/* Shearwater allows entering only 99%, not 100%
* so assume 99% to be pure oxygen */
@ -50,8 +50,8 @@ static int shearwater_changes(void *param, int columns, char **data, char **)
if (!data[0] || !data[1] || !data[2]) {
return 2;
}
int o2 = lrint(strtod_flags(data[1], NULL, 0) * 1000);
int he = lrint(strtod_flags(data[2], NULL, 0) * 1000);
int o2 = lrint(permissive_strtod(data[1], NULL) * 1000);
int he = lrint(permissive_strtod(data[2], NULL) * 1000);
/* Shearwater allows entering only 99%, not 100%
* so assume 99% to be pure oxygen */
@ -101,11 +101,11 @@ static int shearwater_profile_sample(void *param, int, char **data, char **)
if (data[1])
state->cur_sample->depth.mm = state->metric ? lrint(strtod_flags(data[1], NULL, 0) * 1000) : feet_to_mm(strtod_flags(data[1], NULL, 0));
state->cur_sample->depth.mm = state->metric ? lrint(permissive_strtod(data[1], NULL) * 1000) : feet_to_mm(permissive_strtod(data[1], NULL));
if (data[2])
state->cur_sample->temperature.mkelvin = state->metric ? C_to_mkelvin(strtod_flags(data[2], NULL, 0)) : F_to_mkelvin(strtod_flags(data[2], NULL, 0));
state->cur_sample->temperature.mkelvin = state->metric ? C_to_mkelvin(permissive_strtod(data[2], NULL)) : F_to_mkelvin(permissive_strtod(data[2], NULL));
if (data[3]) {
state->cur_sample->setpoint.mbar = lrint(strtod_flags(data[3], NULL, 0) * 1000);
state->cur_sample->setpoint.mbar = lrint(permissive_strtod(data[3], NULL) * 1000);
}
if (data[4])
state->cur_sample->ndl.seconds = atoi(data[4]) * 60;
@ -161,11 +161,11 @@ static int shearwater_ai_profile_sample(void *param, int, char **data, char **)
state->cur_sample->time.seconds = atoi(data[0]);
if (data[1])
state->cur_sample->depth.mm = state->metric ? lrint(strtod_flags(data[1], NULL, 0) * 1000) : feet_to_mm(strtod_flags(data[1], NULL, 0));
state->cur_sample->depth.mm = state->metric ? lrint(permissive_strtod(data[1], NULL) * 1000) : feet_to_mm(permissive_strtod(data[1], NULL));
if (data[2])
state->cur_sample->temperature.mkelvin = state->metric ? C_to_mkelvin(strtod_flags(data[2], NULL, 0)) : F_to_mkelvin(strtod_flags(data[2], NULL, 0));
state->cur_sample->temperature.mkelvin = state->metric ? C_to_mkelvin(permissive_strtod(data[2], NULL)) : F_to_mkelvin(permissive_strtod(data[2], NULL));
if (data[3]) {
state->cur_sample->setpoint.mbar = lrint(strtod_flags(data[3], NULL, 0) * 1000);
state->cur_sample->setpoint.mbar = lrint(permissive_strtod(data[3], NULL) * 1000);
}
if (data[4])
state->cur_sample->ndl.seconds = atoi(data[4]) * 60;
@ -250,7 +250,7 @@ static int shearwater_dive(void *param, int, char **data, char **)
/* TODO: verify that metric calculation is correct */
if (data[6])
state->cur_dive->dc.maxdepth.mm = state->metric ? lrint(strtod_flags(data[6], NULL, 0) * 1000) : feet_to_mm(strtod_flags(data[6], NULL, 0));
state->cur_dive->dc.maxdepth.mm = state->metric ? lrint(permissive_strtod(data[6], NULL) * 1000) : feet_to_mm(permissive_strtod(data[6], NULL));
if (data[7])
state->cur_dive->dc.duration.seconds = atoi(data[7]) * 60;
@ -380,7 +380,7 @@ static int shearwater_cloud_dive(void *param, int, char **data, char **)
/* TODO: verify that metric calculation is correct */
if (data[6])
state->cur_dive->dc.maxdepth.mm = state->metric ? lrint(strtod_flags(data[6], NULL, 0) * 1000) : feet_to_mm(strtod_flags(data[6], NULL, 0));
state->cur_dive->dc.maxdepth.mm = state->metric ? lrint(permissive_strtod(data[6], NULL) * 1000) : feet_to_mm(permissive_strtod(data[6], NULL));
if (data[7])
state->cur_dive->dc.duration.seconds = atoi(data[7]);

View file

@ -208,7 +208,7 @@ static int dm4_dive(void *param, int, char **data, char **)
settings_end(state);
if (data[6])
state->cur_dive->dc.maxdepth.mm = lrint(strtod_flags(data[6], NULL, 0) * 1000);
state->cur_dive->dc.maxdepth.mm = lrint(permissive_strtod(data[6], NULL) * 1000);
if (data[8])
state->cur_dive->dc.airtemp.mkelvin = C_to_mkelvin(atoi(data[8]));
if (data[9])
@ -227,7 +227,7 @@ static int dm4_dive(void *param, int, char **data, char **)
if (data[11] && atoi(data[11]) > 0)
cyl->end.mbar = (atoi(data[11]));
if (data[12])
cyl->type.size.mliter = lrint((strtod_flags(data[12], NULL, 0)) * 1000);
cyl->type.size.mliter = lrint((permissive_strtod(data[12], NULL)) * 1000);
if (data[13])
cyl->type.workingpressure.mbar = (atoi(data[13]));
if (data[20])
@ -314,10 +314,10 @@ static int dm5_cylinders(void *param, int, char **data, char **)
/* DM5 shows tank size of 12 liters when the actual
* value is 0 (and using metric units). So we just use
* the same 12 liters when size is not available */
if (strtod_flags(data[6], NULL, 0) == 0.0 && cyl->start.mbar)
if (permissive_strtod(data[6], NULL) == 0.0 && cyl->start.mbar)
cyl->type.size.mliter = 12000;
else
cyl->type.size.mliter = lrint((strtod_flags(data[6], NULL, 0)) * 1000);
cyl->type.size.mliter = lrint((permissive_strtod(data[6], NULL)) * 1000);
}
if (data[2])
cyl->gasmix.o2.permille = atoi(data[2]) * 10;
@ -336,12 +336,12 @@ static int dm5_gaschange(void *param, int, char **data, char **)
state->cur_event.time.seconds = atoi(data[0]);
if (data[1]) {
strcpy(state->cur_event.name, "gaschange");
state->cur_event.value = lrint(strtod_flags(data[1], NULL, 0));
state->cur_event.value = lrint(permissive_strtod(data[1], NULL));
}
/* He part of the mix */
if (data[2])
state->cur_event.value += lrint(strtod_flags(data[2], NULL, 0)) << 16;
state->cur_event.value += lrint(permissive_strtod(data[2], NULL)) << 16;
event_end(state);
return 0;
@ -389,7 +389,7 @@ static int dm5_dive(void *param, int, char **data, char **)
settings_end(state);
if (data[6])
state->cur_dive->dc.maxdepth.mm = lrint(strtod_flags(data[6], NULL, 0) * 1000);
state->cur_dive->dc.maxdepth.mm = lrint(permissive_strtod(data[6], NULL) * 1000);
if (data[8])
state->cur_dive->dc.airtemp.mkelvin = C_to_mkelvin(atoi(data[8]));
if (data[9])

View file

@ -168,7 +168,7 @@ static enum number_type parse_float(const char *buffer, double &res, const char
first_time = false;
}
/* Try again in permissive mode*/
val = strtod_flags(buffer, &endp, 0);
val = permissive_strtod(buffer, &endp);
}
}

View file

@ -1240,7 +1240,7 @@ QStringList stringToList(const QString &s)
weight_t string_to_weight(const char *str)
{
const char *end;
double value = strtod_flags(str, &end, 0);
double value = permissive_strtod(str, &end);
QString rest = QString(end).trimmed();
QString local_kg = gettextFromC::tr("kg");
QString local_lbs = gettextFromC::tr("lbs");
@ -1264,7 +1264,7 @@ lbs:
depth_t string_to_depth(const char *str)
{
const char *end;
double value = strtod_flags(str, &end, 0);
double value = permissive_strtod(str, &end);
QString rest = QString(end).trimmed();
QString local_ft = gettextFromC::tr("ft");
QString local_m = gettextFromC::tr("m");
@ -1289,7 +1289,7 @@ ft:
pressure_t string_to_pressure(const char *str)
{
const char *end;
double value = strtod_flags(str, &end, 0);
double value = permissive_strtod(str, &end);
QString rest = QString(end).trimmed();
QString local_psi = gettextFromC::tr("psi");
QString local_bar = gettextFromC::tr("bar");
@ -1312,7 +1312,7 @@ psi:
volume_t string_to_volume(const char *str, pressure_t workp)
{
const char *end;
double value = strtod_flags(str, &end, 0);
double value = permissive_strtod(str, &end);
QString rest = QString(end).trimmed();
QString local_l = gettextFromC::tr("l");
QString local_cuft = gettextFromC::tr("cuft");
@ -1343,7 +1343,7 @@ l:
fraction_t string_to_fraction(const char *str)
{
const char *end;
double value = strtod_flags(str, &end, 0);
double value = permissive_strtod(str, &end);
fraction_t fraction;
fraction.permille = lrint(value * 10);

View file

@ -3,68 +3,51 @@
* 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
* insane locale behavior, and while the Qt "toDouble()" 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.
* So if you want the C locale kind of parsing, use the
* ascii_strtod() function. But if you want a more relaxed
* "Hey, Europeans are people too, even if they have locales
* with commas", use general_strtod() instead.
*/
#include <ctype.h>
#include "subsurface-string.h"
double strtod_flags(const char *str, const char **ptr, unsigned int flags)
static double strtod_flags(const char *str, const char **ptr, bool no_comma)
{
char c;
const char *p = str, *ep;
double val = 0.0;
double decimal = 1.0;
int sign = 0, esign = 0;
int numbers = 0, dot = 0;
bool sign = false, esign = false;
bool numbers = false, dot = false;
/* skip spaces */
while (isspace(c = *p++))
/* */;
/* optional sign */
if (!(flags & STRTOD_NO_SIGN)) {
switch (c) {
case '-':
sign = 1;
/* fallthrough */
case '+':
c = *p++;
}
switch (c) {
case '-':
sign = true;
/* fallthrough */
case '+':
c = *p++;
}
/* Mantissa */
for (;; c = *p++) {
if ((c == '.' && !(flags & STRTOD_NO_DOT)) ||
(c == ',' && !(flags & STRTOD_NO_COMMA))) {
if (c == '.' || (c == ',' && !no_comma)) {
if (dot)
goto done;
dot = 1;
dot = true;
continue;
}
if (c >= '0' && c <= '9') {
numbers++;
numbers = true;
val = (val * 10) + (c - '0');
if (dot)
decimal *= 10;
@ -72,8 +55,6 @@ double strtod_flags(const char *str, const char **ptr, unsigned int flags)
}
if (c != 'e' && c != 'E')
goto done;
if (flags & STRTOD_NO_EXPONENT)
goto done;
break;
}
@ -85,7 +66,7 @@ double strtod_flags(const char *str, const char **ptr, unsigned int flags)
c = *ep++;
switch (c) {
case '-':
esign = 1;
esign = true;
/* fallthrough */
case '+':
c = *ep++;
@ -127,3 +108,13 @@ no_conversion:
*ptr = str;
return 0.0;
}
double permissive_strtod(const char *str, const char **ptr)
{
return strtod_flags(str, ptr, false);
}
double ascii_strtod(const char *str, const char **ptr)
{
return strtod_flags(str, ptr, true);
}

View file

@ -52,15 +52,8 @@ static inline char *copy_string(const char *s)
return (s && *s) ? strdup(s) : NULL;
}
#define STRTOD_NO_SIGN 0x01
#define STRTOD_NO_DOT 0x02
#define STRTOD_NO_COMMA 0x04
#define STRTOD_NO_EXPONENT 0x08
extern double strtod_flags(const char *str, const char **ptr, unsigned int flags);
#define STRTOD_ASCII (STRTOD_NO_COMMA)
#define ascii_strtod(str, ptr) strtod_flags(str, ptr, STRTOD_ASCII)
extern double permissive_strtod(const char *str, const char **ptr);
extern double ascii_strtod(const char *str, const char **ptr);
#ifdef __cplusplus
}