From 9826dd0f7cc53881de23fac317a7190e47f78401 Mon Sep 17 00:00:00 2001 From: Grigori Goronzy Date: Sun, 3 Jan 2010 14:54:02 +0100 Subject: Replace strtod with locale-independent strtod strtod respects the locale and in some locales, the decimal separator is not a point, leading to parsing errors in tags like \pos(23.4,5), which are perfectly valid. As there isn't a really portable way to use a particular locale just for one call to strtod, reimplement it. The implementation was taken from the 1.8 branch of Ruby. --- libass/Makefile.am | 2 +- libass/ass_strtod.c | 247 ++++++++++++++++++++++++++++++++++++++++++++++++++++ libass/ass_utils.c | 6 +- libass/ass_utils.h | 3 + 4 files changed, 254 insertions(+), 4 deletions(-) create mode 100644 libass/ass_strtod.c diff --git a/libass/Makefile.am b/libass/Makefile.am index 0cf613e..10b5c5b 100644 --- a/libass/Makefile.am +++ b/libass/Makefile.am @@ -8,7 +8,7 @@ libass_la_SOURCES = ass.c ass_cache.c ass_font.c ass_fontconfig.c ass_render.c \ ass_cache.h ass_fontconfig.h ass_font.h ass.h \ ass_library.h ass_types.h ass_utils.h ass_drawing.c \ ass_drawing.h ass_cache_template.h ass_render.h \ - ass_parse.c ass_parse.h + ass_parse.c ass_parse.h ass_strtod.c libass_la_LDFLAGS = -version-info $(LIBASS_LT_CURRENT):$(LIBASS_LT_REVISION):$(LIBASS_LT_AGE) libass_la_LDFLAGS += -export-symbols $(srcdir)/libass.sym diff --git a/libass/ass_strtod.c b/libass/ass_strtod.c new file mode 100644 index 0000000..7b73630 --- /dev/null +++ b/libass/ass_strtod.c @@ -0,0 +1,247 @@ +/* + * Copyright (c) 1988-1993 The Regents of the University of California. + * Copyright (c) 1994 Sun Microsystems, Inc. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies. The University of California + * makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without + * express or implied warranty. + * + */ + +#include +#include +#include + +static int maxExponent = 511; /* Largest possible base 10 exponent. Any + * exponent larger than this will already + * produce underflow or overflow, so there's + * no need to worry about additional digits. + */ + +static double powersOf10[] = { /* Table giving binary powers of 10. Entry */ + 10., /* is 10^2^i. Used to convert decimal */ + 100., /* exponents into floating-point numbers. */ + 1.0e4, + 1.0e8, + 1.0e16, + 1.0e32, + 1.0e64, + 1.0e128, + 1.0e256 +}; + +/* + *---------------------------------------------------------------------- + * + * strtod -- + * + * This procedure converts a floating-point number from an ASCII + * decimal representation to internal double-precision format. + * + * Results: + * The return value is the double-precision floating-point + * representation of the characters in string. If endPtr isn't + * NULL, then *endPtr is filled in with the address of the + * next character after the last one that was part of the + * floating-point number. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +double +ass_strtod(string, endPtr) + const char *string; /* A decimal ASCII floating-point number, + * optionally preceded by white space. + * Must have form "-I.FE-X", where I is the + * integer part of the mantissa, F is the + * fractional part of the mantissa, and X + * is the exponent. Either of the signs + * may be "+", "-", or omitted. Either I + * or F may be omitted, or both. The decimal + * point isn't necessary unless F is present. + * The "E" may actually be an "e". E and X + * may both be omitted (but not just one). + */ + char **endPtr; /* If non-NULL, store terminating character's + * address here. */ +{ + int sign, expSign = 0; + double fraction, dblExp, *d; + register const char *p; + register int c; + int exp = 0; /* Exponent read from "EX" field. */ + int fracExp = 0; /* Exponent that derives from the fractional + * part. Under normal circumstatnces, it is + * the negative of the number of digits in F. + * However, if I is very long, the last digits + * of I get dropped (otherwise a long I with a + * large negative exponent could cause an + * unnecessary overflow on I alone). In this + * case, fracExp is incremented one for each + * dropped digit. */ + int mantSize; /* Number of digits in mantissa. */ + int decPt; /* Number of mantissa digits BEFORE decimal + * point. */ + const char *pExp; /* Temporarily holds location of exponent + * in string. */ + + /* + * Strip off leading blanks and check for a sign. + */ + + p = string; + while (isspace(*p)) { + p += 1; + } + if (*p == '-') { + sign = 1; + p += 1; + } else { + if (*p == '+') { + p += 1; + } + sign = 0; + } + + /* + * Count the number of digits in the mantissa (including the decimal + * point), and also locate the decimal point. + */ + + decPt = -1; + for (mantSize = 0; ; mantSize += 1) + { + c = *p; + if (!isdigit(c)) { + if ((c != '.') || (decPt >= 0)) { + break; + } + decPt = mantSize; + } + p += 1; + } + + /* + * Now suck up the digits in the mantissa. Use two integers to + * collect 9 digits each (this is faster than using floating-point). + * If the mantissa has more than 18 digits, ignore the extras, since + * they can't affect the value anyway. + */ + + pExp = p; + p -= mantSize; + if (decPt < 0) { + decPt = mantSize; + } else { + mantSize -= 1; /* One of the digits was the point. */ + } + if (mantSize > 18) { + fracExp = decPt - 18; + mantSize = 18; + } else { + fracExp = decPt - mantSize; + } + if (mantSize == 0) { + fraction = 0.0; + p = string; + goto done; + } else { + int frac1, frac2; + frac1 = 0; + for ( ; mantSize > 9; mantSize -= 1) + { + c = *p; + p += 1; + if (c == '.') { + c = *p; + p += 1; + } + frac1 = 10*frac1 + (c - '0'); + } + frac2 = 0; + for (; mantSize > 0; mantSize -= 1) + { + c = *p; + p += 1; + if (c == '.') { + c = *p; + p += 1; + } + frac2 = 10*frac2 + (c - '0'); + } + fraction = (1.0e9 * frac1) + frac2; + } + + /* + * Skim off the exponent. + */ + + p = pExp; + if ((*p == 'E') || (*p == 'e')) { + p += 1; + if (*p == '-') { + expSign = 1; + p += 1; + } else { + if (*p == '+') { + p += 1; + } + expSign = 0; + } + while (isdigit(*p)) { + exp = exp * 10 + (*p - '0'); + p += 1; + } + } + if (expSign) { + exp = fracExp - exp; + } else { + exp = fracExp + exp; + } + + /* + * Generate a floating-point number that represents the exponent. + * Do this by processing the exponent one bit at a time to combine + * many powers of 2 of 10. Then combine the exponent with the + * fraction. + */ + + if (exp < 0) { + expSign = 1; + exp = -exp; + } else { + expSign = 0; + } + if (exp > maxExponent) { + exp = maxExponent; + errno = ERANGE; + } + dblExp = 1.0; + for (d = powersOf10; exp != 0; exp >>= 1, d += 1) { + if (exp & 01) { + dblExp *= *d; + } + } + if (expSign) { + fraction /= dblExp; + } else { + fraction *= dblExp; + } + +done: + if (endPtr != NULL) { + *endPtr = (char *) p; + } + + if (sign) { + return -fraction; + } + return fraction; +} diff --git a/libass/ass_utils.c b/libass/ass_utils.c index 6ca78b8..59fdbdf 100644 --- a/libass/ass_utils.c +++ b/libass/ass_utils.c @@ -34,7 +34,7 @@ int mystrtoi(char **p, int *res) { double temp_res; char *start = *p; - temp_res = strtod(*p, p); + temp_res = ass_strtod(*p, p); *res = (int) (temp_res + (temp_res > 0 ? 0.5 : -0.5)); if (*p != start) return 1; @@ -46,7 +46,7 @@ int mystrtoll(char **p, long long *res) { double temp_res; char *start = *p; - temp_res = strtod(*p, p); + temp_res = ass_strtod(*p, p); *res = (int) (temp_res + (temp_res > 0 ? 0.5 : -0.5)); if (*p != start) return 1; @@ -67,7 +67,7 @@ int mystrtou32(char **p, int base, uint32_t *res) int mystrtod(char **p, double *res) { char *start = *p; - *res = strtod(*p, p); + *res = ass_strtod(*p, p); if (*p != start) return 1; else diff --git a/libass/ass_utils.h b/libass/ass_utils.h index bade578..f5c68e9 100644 --- a/libass/ass_utils.h +++ b/libass/ass_utils.h @@ -59,6 +59,9 @@ void *ass_guess_buffer_cp(ASS_Library *library, unsigned char *buffer, char *fallback); #endif +/* defined in ass_strtod.c */ +double ass_strtod(const char *string, char **endPtr); + static inline int d6_to_int(int x) { return (x + 32) >> 6; -- cgit v1.2.3