]> granicus.if.org Git - libass/commitdiff
Replace strtod with locale-independent strtod
authorGrigori Goronzy <greg@blackbox>
Sun, 3 Jan 2010 13:54:02 +0000 (14:54 +0100)
committerGrigori Goronzy <greg@blackbox>
Tue, 5 Jan 2010 22:52:02 +0000 (23:52 +0100)
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
libass/ass_strtod.c [new file with mode: 0644]
libass/ass_utils.c
libass/ass_utils.h

index 0cf613ee1fe542c1265240421c270b11a5d6cc81..10b5c5b325eb235fd3a092bbba172faf6c94fe58 100644 (file)
@@ -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 (file)
index 0000000..7b73630
--- /dev/null
@@ -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 <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+
+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;
+}
index 6ca78b860d4710c5610141cec01515bc2c5cb967..59fdbdfb94e4d195d330c3957fe23556d2908a90 100644 (file)
@@ -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
index bade57804dc5a2dca6bc8f7a495310e23bb52671..f5c68e96cf634b10405cd6b0d84936dba551f8a7 100644 (file)
@@ -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;