]> git.saurik.com Git - apple/libc.git/blobdiff - gdtoa/FreeBSD/gdtoa-gethex.c
Libc-1272.200.26.tar.gz
[apple/libc.git] / gdtoa / FreeBSD / gdtoa-gethex.c
index fadb5de812602cd1c6e7eee1e7d21a9bfa7951f7..fd0775c2a298c9a76fc39b62dcbdadc1c0295ae8 100644 (file)
@@ -26,38 +26,55 @@ THIS SOFTWARE.
 
 ****************************************************************/
 
-/* Please send bug reports to
-       David M. Gay
-       dmg@acm.org
- */
+/* Please send bug reports to David M. Gay (dmg at acm dot org,
+ * with " at " changed at "@" and " dot " changed to ".").     */
+
+#include "xlocale_private.h"
 
 #include "gdtoaimp.h"
 
+#include <sys/types.h>
+
 #ifdef USE_LOCALE
 #include "locale.h"
 #endif
 
  int
 #ifdef KR_headers
-gethex(sp, fpi, exp, bp, sign)
-       CONST char **sp; FPI *fpi; Long *exp; Bigint **bp; int sign;
+gethex(sp, fpi, exp, bp, sign, loc)
+       CONST char **sp; CONST FPI *fpi; Long *exp; Bigint **bp; int sign; locale_t loc;
 #else
-gethex( CONST char **sp, FPI *fpi, Long *exp, Bigint **bp, int sign)
+gethex( CONST char **sp, CONST FPI *fpi, Long *exp, Bigint **bp, int sign, locale_t loc)
 #endif
 {
        Bigint *b;
        CONST unsigned char *decpt, *s0, *s, *s1;
-       int esign, havedig, irv, k, n, nbits, up;
+       unsigned char *strunc;
+       int big, esign, havedig, irv, j, k, n, n0, nbits, up, zret;
        ULong L, lostbits, *x;
        Long e, e1;
 #ifdef USE_LOCALE
-       unsigned char decimalpoint = *localeconv()->decimal_point;
+       int i;
+       NORMALIZE_LOCALE(loc);
+#ifdef NO_LOCALE_CACHE
+       const unsigned char *decimalpoint = (unsigned char*)localeconv_l(loc)->decimal_point;
 #else
-#define decimalpoint '.'
+       const unsigned char *decimalpoint;
+       static unsigned char *decimalpoint_cache;
+       if (!(s0 = decimalpoint_cache)) {
+               s0 = (unsigned char*)localeconv_l(loc)->decimal_point;
+               if ((decimalpoint_cache = (char*)MALLOC(strlen(s0) + 1))) {
+                       strcpy(decimalpoint_cache, s0);
+                       s0 = decimalpoint_cache;
+                       }
+               }
+       decimalpoint = s0;
+#endif
 #endif
 
        if (!hexdig['0'])
                hexdig_init_D2A();
+       *bp = 0;
        havedig = 0;
        s0 = *(CONST unsigned char **)sp + 2;
        while(s0[havedig] == '0')
@@ -65,39 +82,56 @@ gethex( CONST char **sp, FPI *fpi, Long *exp, Bigint **bp, int sign)
        s0 += havedig;
        s = s0;
        decpt = 0;
-       if (!hexdig[*s]) {
-               if (*s == decimalpoint) {
-                       decpt = ++s;
-                       if (!hexdig[*s])
-                               goto ret0;
-                       }
-               else {
- ret0:
-                       *sp = (char*)s;
-                       return havedig ? STRTOG_Zero : STRTOG_NoNumber;
+       zret = 0;
+       e = 0;
+       if (hexdig[*s])
+               havedig++;
+       else {
+               zret = 1;
+#ifdef USE_LOCALE
+               for(i = 0; decimalpoint[i]; ++i) {
+                       if (s[i] != decimalpoint[i])
+                               goto pcheck;
                        }
+               decpt = s += i;
+#else
+               if (*s != '.')
+                       goto pcheck;
+               decpt = ++s;
+#endif
+               if (!hexdig[*s])
+                       goto pcheck;
                while(*s == '0')
                        s++;
+               if (hexdig[*s])
+                       zret = 0;
                havedig = 1;
-               if (!hexdig[*s])
-                       goto ret0;
                s0 = s;
                }
        while(hexdig[*s])
                s++;
-       if (*s == decimalpoint && !decpt) {
+#ifdef USE_LOCALE
+       if (*s == *decimalpoint && !decpt) {
+               for(i = 1; decimalpoint[i]; ++i) {
+                       if (s[i] != decimalpoint[i])
+                               goto pcheck;
+                       }
+               decpt = s += i;
+#else
+       if (*s == '.' && !decpt) {
                decpt = ++s;
+#endif
                while(hexdig[*s])
                        s++;
-               }
-       e = 0;
+               }/*}*/
        if (decpt)
                e = -(((Long)(s-decpt)) << 2);
+ pcheck:
        s1 = s;
+       big = esign = 0;
        switch(*s) {
          case 'p':
          case 'P':
-               esign = 0;
                switch(*++s) {
                  case '-':
                        esign = 1;
@@ -110,24 +144,138 @@ gethex( CONST char **sp, FPI *fpi, Long *exp, Bigint **bp, int sign)
                        break;
                        }
                e1 = n - 0x10;
-               while((n = hexdig[*++s]) !=0 && n <= 0x19)
+               while((n = hexdig[*++s]) !=0 && n <= 0x19) {
+                       if (e1 & 0xf8000000)
+                               big = 1;
                        e1 = 10*e1 + n - 0x10;
+                       }
                if (esign)
                        e1 = -e1;
                e += e1;
          }
        *sp = (char*)s;
+       if (!havedig)
+               *sp = (char*)s0 - 1;
+       if (zret)
+               return STRTOG_Zero;
+       if (big) {
+               if (esign) {
+                       switch(fpi->rounding) {
+                         case FPI_Round_up:
+                               if (sign)
+                                       break;
+                               goto ret_tiny;
+                         case FPI_Round_down:
+                               if (!sign)
+                                       break;
+                               goto ret_tiny;
+                         }
+                       goto retz;
+ ret_tiny:
+                       b = Balloc(0);
+                       b->wds = 1;
+                       b->x[0] = 1;
+                       goto dret;
+                       }
+               switch(fpi->rounding) {
+                 case FPI_Round_near:
+                       goto ovfl1;
+                 case FPI_Round_up:
+                       if (!sign)
+                               goto ovfl1;
+                       goto ret_big;
+                 case FPI_Round_down:
+                       if (sign)
+                               goto ovfl1;
+                       goto ret_big;
+                 }
+ ret_big:
+               nbits = fpi->nbits;
+               n0 = n = nbits >> kshift;
+               if (nbits & kmask)
+                       ++n;
+               for(j = n, k = 0; j >>= 1; ++k);
+               *bp = b = Balloc(k);
+               b->wds = n;
+               for(j = 0; j < n0; ++j)
+                       b->x[j] = ALL_ON;
+               if (n > n0)
+                       b->x[j] = ULbits >> (ULbits - (nbits & kmask));
+               *exp = fpi->emin;
+               return STRTOG_Normal | STRTOG_Inexlo;
+               }
+       /*
+        * Truncate the hex string if it is longer than the precision needed,
+        * to avoid denial-of-service issues with very large strings.  Use
+        * additional digits to insure precision.  Scan to-be-truncated digits
+        * and replace with either '1' or '0' to ensure proper rounding.
+        */
+       {
+               int maxdigits = ((fpi->nbits + 3) >> 2) + 2;
+               size_t nd = s1 - s0;
+#ifdef USE_LOCALE
+               int dplen = strlen((const char *)decimalpoint);
+#else
+               int dplen = 1;
+#endif
+
+               if (decpt && s0 < decpt)
+                       nd -= dplen;
+               if (nd > maxdigits && (strunc = alloca(maxdigits + dplen + 2)) != NULL) {
+                       ssize_t nd0 = decpt ? decpt - s0 - dplen : nd;
+                       unsigned char *tp = strunc + maxdigits;
+                       int found = 0;
+                       if ((nd0 -= maxdigits) >= 0 || s0 >= decpt)
+                               memcpy(strunc, s0, maxdigits);
+                       else {
+                               memcpy(strunc, s0, maxdigits + dplen);
+                               tp += dplen;
+                               }
+                       s0 += maxdigits;
+                       e += (nd - (maxdigits + 1)) << 2;
+                       if (nd0 > 0) {
+                               while(nd0-- > 0)
+                                       if (*s0++ != '0') {
+                                               found++;
+                                               break;
+                                               }
+                               s0 += dplen;
+                               }
+                       if (!found && decpt) {
+                               while(s0 < s1)
+                                       if(*s0++ != '0') {
+                                               found++;
+                                               break;
+                                               }
+                               }
+                       *tp++ = found ? '1' : '0';
+                       *tp = 0;
+                       s0 = strunc;
+                       s1 = tp;
+                       }
+               }
+
        n = s1 - s0 - 1;
-       for(k = 0; n > 7; n >>= 1)
+       for(k = 0; n > (1 << (kshift-2)) - 1; n >>= 1)
                k++;
        b = Balloc(k);
        x = b->x;
        n = 0;
        L = 0;
+#ifdef USE_LOCALE
+       for(i = 0; decimalpoint[i+1]; ++i);
+#endif
        while(s1 > s0) {
-               if (*--s1 == decimalpoint)
+#ifdef USE_LOCALE
+               if (*--s1 == decimalpoint[i]) {
+                       s1 -= i;
+                       continue;
+                       }
+#else
+               if (*--s1 == '.')
                        continue;
-               if (n == 32) {
+#endif
+               if (n == ULbits) {
                        *x++ = L;
                        L = 0;
                        n = 0;
@@ -137,7 +285,7 @@ gethex( CONST char **sp, FPI *fpi, Long *exp, Bigint **bp, int sign)
                }
        *x++ = L;
        b->wds = n = x - b->x;
-       n = 32*n - hi0bits(L);
+       n = ULbits*n - hi0bits(L);
        nbits = fpi->nbits;
        lostbits = 0;
        x = b->x;
@@ -148,7 +296,7 @@ gethex( CONST char **sp, FPI *fpi, Long *exp, Bigint **bp, int sign)
                        k = n - 1;
                        if (x[k>>kshift] & 1 << (k & kmask)) {
                                lostbits = 2;
-                               if (k > 1 && any_on(b,k-1))
+                               if (k > 0 && any_on(b,k))
                                        lostbits = 3;
                                }
                        }
@@ -164,7 +312,10 @@ gethex( CONST char **sp, FPI *fpi, Long *exp, Bigint **bp, int sign)
        if (e > fpi->emax) {
  ovfl:
                Bfree(b);
-               *bp = 0;
+ ovfl1:
+#ifndef NO_ERRNO
+               errno = ERANGE;
+#endif
                return STRTOG_Infinite | STRTOG_Overflow | STRTOG_Inexhi;
                }
        irv = STRTOG_Normal;
@@ -184,15 +335,22 @@ gethex( CONST char **sp, FPI *fpi, Long *exp, Bigint **bp, int sign)
                          case FPI_Round_down:
                                if (sign) {
  one_bit:
-                                       *exp = fpi->emin;
                                        x[0] = b->wds = 1;
+ dret:
                                        *bp = b;
+                                       *exp = fpi->emin;
+#ifndef NO_ERRNO
+                                       errno = ERANGE;
+#endif
                                        return STRTOG_Denormal | STRTOG_Inexhi
                                                | STRTOG_Underflow;
                                        }
                          }
                        Bfree(b);
-                       *bp = 0;
+ retz:
+#ifndef NO_ERRNO
+                       errno = ERANGE;
+#endif
                        return STRTOG_Zero | STRTOG_Inexlo | STRTOG_Underflow;
                        }
                k = n - 1;
@@ -213,7 +371,7 @@ gethex( CONST char **sp, FPI *fpi, Long *exp, Bigint **bp, int sign)
                        break;
                  case FPI_Round_near:
                        if (lostbits & 2
-                        && (lostbits & 1) | x[0] & 1)
+                        && (lostbits | x[0]) & 1)
                                up = 1;
                        break;
                  case FPI_Round_up:
@@ -232,8 +390,8 @@ gethex( CONST char **sp, FPI *fpi, Long *exp, Bigint **bp, int sign)
                                        irv =  STRTOG_Normal;
                                }
                        else if (b->wds > k
-                        || (n = nbits & kmask) !=0
-                            && hi0bits(x[k-1]) < 32-n) {
+                        || ((n = nbits & kmask) !=0
+                             && hi0bits(x[k-1]) < 32-n)) {
                                rshift(b,1);
                                if (++e > fpi->emax)
                                        goto ovfl;