// SPDX-License-Identifier: GPL-2.0 /* * Sane helper for 'strtod()'. * * Sad that we even need this, but the C library version has * 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). * * 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 #include "subsurface-string.h" 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; bool sign = false, esign = false; bool numbers = false, dot = false; /* skip spaces */ while (isspace(c = *p++)) /* */; /* optional sign */ switch (c) { case '-': sign = true; /* fallthrough */ case '+': c = *p++; } /* Mantissa */ for (;; c = *p++) { if (c == '.' || (c == ',' && !no_comma)) { if (dot) goto done; dot = true; continue; } if (c >= '0' && c <= '9') { numbers = true; val = (val * 10) + (c - '0'); if (dot) decimal *= 10; continue; } if (c != 'e' && c != 'E') goto done; break; } if (!numbers) goto done; /* Exponent */ ep = p; c = *ep++; switch (c) { case '-': esign = true; /* 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) decimal *= 10; else decimal /= 10; } } done: if (!numbers) goto no_conversion; if (ptr) *ptr = p - 1; return (sign ? -val : val) / decimal; no_conversion: if (ptr) *ptr = str; return 0.0; } extern "C" double permissive_strtod(const char *str, const char **ptr) { return strtod_flags(str, ptr, false); } extern "C" double ascii_strtod(const char *str, const char **ptr) { return strtod_flags(str, ptr, true); }