]> git.cworth.org Git - tar/blobdiff - lib/vasnprintf.c
Imported Upstream version 1.21
[tar] / lib / vasnprintf.c
index 3bdb4e97a49e695dd14f77e4f66a1e0f12590071..426b7c51be2434b4214bc35b7f6ab25b421fdee0 100644 (file)
@@ -97,7 +97,7 @@
 
 #if (NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE) && !defined IN_LIBINTL
 # include <math.h>
-# include "isnand.h"
+# include "isnand-nolibm.h"
 #endif
 
 #if (NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE) && !defined IN_LIBINTL
 
 #if (NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_DOUBLE) && !defined IN_LIBINTL
 # include <math.h>
-# include "isnand.h"
+# include "isnand-nolibm.h"
 # include "printf-frexp.h"
 #endif
 
@@ -179,10 +179,12 @@ local_wcslen (const wchar_t *s)
 # endif
 #else
   /* TCHAR_T is char.  */
-# /* Use snprintf if it exists under the name 'snprintf' or '_snprintf'.
+  /* Use snprintf if it exists under the name 'snprintf' or '_snprintf'.
      But don't use it on BeOS, since BeOS snprintf produces no output if the
-     size argument is >= 0x3000000.  */
-# if (HAVE_DECL__SNPRINTF || HAVE_SNPRINTF) && !defined __BEOS__
+     size argument is >= 0x3000000.
+     Also don't use it on Linux libc5, since there snprintf with size = 1
+     writes any output without bounds, like sprintf.  */
+# if (HAVE_DECL__SNPRINTF || HAVE_SNPRINTF) && !defined __BEOS__ && !(__GNU_LIBRARY__ == 1)
 #  define USE_SNPRINTF 1
 # else
 #  define USE_SNPRINTF 0
@@ -200,7 +202,22 @@ local_wcslen (const wchar_t *s)
 /* Here we need to call the native sprintf, not rpl_sprintf.  */
 #undef sprintf
 
-#if (NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE) && !defined IN_LIBINTL
+/* GCC >= 4.0 with -Wall emits unjustified "... may be used uninitialized"
+   warnings in this file.  Use -Dlint to suppress them.  */
+#ifdef lint
+# define IF_LINT(Code) Code
+#else
+# define IF_LINT(Code) /* empty */
+#endif
+
+/* Avoid some warnings from "gcc -Wshadow".
+   This file doesn't use the exp() and remainder() functions.  */
+#undef exp
+#define exp expo
+#undef remainder
+#define remainder rem
+
+#if (NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE || NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE) && !defined IN_LIBINTL
 /* Determine the decimal-point character according to the current locale.  */
 # ifndef decimal_point_char_defined
 #  define decimal_point_char_defined 1
@@ -240,11 +257,11 @@ is_infinite_or_zero (double x)
 
 #if NEED_PRINTF_INFINITE_LONG_DOUBLE && !NEED_PRINTF_LONG_DOUBLE && !defined IN_LIBINTL
 
-/* Equivalent to !isfinite(x), but does not require libm.  */
+/* Equivalent to !isfinite(x) || x == 0, but does not require libm.  */
 static int
-is_infinitel (long double x)
+is_infinite_or_zerol (long double x)
 {
-  return isnanl (x) || (x + x == x && x != 0.0L);
+  return isnanl (x) || x + x == x;
 }
 
 #endif
@@ -1198,7 +1215,7 @@ scale10_round_decimal_decoded (int e, mpn_t m, void *memory, int n)
 static char *
 scale10_round_decimal_long_double (long double x, int n)
 {
-  int e;
+  int e IF_LINT(= 0);
   mpn_t m;
   void *memory = decode_long_double (x, &e, &m);
   return scale10_round_decimal_decoded (e, m, memory, n);
@@ -1216,7 +1233,7 @@ scale10_round_decimal_long_double (long double x, int n)
 static char *
 scale10_round_decimal_double (double x, int n)
 {
-  int e;
+  int e IF_LINT(= 0);
   mpn_t m;
   void *memory = decode_double (x, &e, &m);
   return scale10_round_decimal_decoded (e, m, memory, n);
@@ -1303,9 +1320,9 @@ floorlog10l (long double x)
     }
   /* Now 0.95 <= z <= 1.01.  */
   z = 1 - z;
-  /* log(1-z) = - z - z^2/2 - z^3/3 - z^4/4 - ...
+  /* log2(1-z) = 1/log(2) * (- z - z^2/2 - z^3/3 - z^4/4 - ...)
      Four terms are enough to get an approximation with error < 10^-7.  */
-  l -= z * (1.0 + z * (0.5 + z * ((1.0 / 3) + z * 0.25)));
+  l -= 1.4426950408889634074 * z * (1.0 + z * (0.5 + z * ((1.0 / 3) + z * 0.25)));
   /* Finally multiply with log(2)/log(10), yields an approximation for
      log10(x).  */
   l *= 0.30102999566398119523;
@@ -1394,9 +1411,9 @@ floorlog10 (double x)
     }
   /* Now 0.95 <= z <= 1.01.  */
   z = 1 - z;
-  /* log(1-z) = - z - z^2/2 - z^3/3 - z^4/4 - ...
+  /* log2(1-z) = 1/log(2) * (- z - z^2/2 - z^3/3 - z^4/4 - ...)
      Four terms are enough to get an approximation with error < 10^-7.  */
-  l -= z * (1.0 + z * (0.5 + z * ((1.0 / 3) + z * 0.25)));
+  l -= 1.4426950408889634074 * z * (1.0 + z * (0.5 + z * ((1.0 / 3) + z * 0.25)));
   /* Finally multiply with log(2)/log(10), yields an approximation for
      log10(x).  */
   l *= 0.30102999566398119523;
@@ -1406,6 +1423,20 @@ floorlog10 (double x)
 
 # endif
 
+/* Tests whether a string of digits consists of exactly PRECISION zeroes and
+   a single '1' digit.  */
+static int
+is_borderline (const char *digits, size_t precision)
+{
+  for (; precision > 0; precision--, digits++)
+    if (*digits != '0')
+      return 0;
+  if (*digits != '1')
+    return 0;
+  digits++;
+  return *digits == '\0';
+}
+
 #endif
 
 DCHAR_T *
@@ -2549,8 +2580,10 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
 # elif NEED_PRINTF_INFINITE_LONG_DOUBLE
                         || (a.arg[dp->arg_index].type == TYPE_LONGDOUBLE
                             /* Some systems produce wrong output for Inf,
-                               -Inf, and NaN.  */
-                            && is_infinitel (a.arg[dp->arg_index].a.a_longdouble))
+                               -Inf, and NaN.  Some systems in this category
+                               (IRIX 5.3) also do so for -0.0.  Therefore we
+                               treat this case here as well.  */
+                            && is_infinite_or_zerol (a.arg[dp->arg_index].a.a_longdouble))
 # endif
                        ))
              {
@@ -2632,9 +2665,11 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
 
                /* POSIX specifies the default precision to be 6 for %f, %F,
                   %e, %E, but not for %g, %G.  Implementations appear to use
-                  the same default precision also for %g, %G.  */
+                  the same default precision also for %g, %G.  But for %a, %A,
+                  the default precision is 0.  */
                if (!has_precision)
-                 precision = 6;
+                 if (!(dp->conversion == 'a' || dp->conversion == 'A'))
+                   precision = 6;
 
                /* Allocate a temporary buffer of sufficient size.  */
 # if NEED_PRINTF_DOUBLE && NEED_PRINTF_LONG_DOUBLE
@@ -2855,8 +2890,32 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
                                          exponent += 1;
                                        adjusted = 1;
                                      }
-
                                    /* Here ndigits = precision+1.  */
+                                   if (is_borderline (digits, precision))
+                                     {
+                                       /* Maybe the exponent guess was too high
+                                          and a smaller exponent can be reached
+                                          by turning a 10...0 into 9...9x.  */
+                                       char *digits2 =
+                                         scale10_round_decimal_long_double (arg,
+                                                                            (int)precision - exponent + 1);
+                                       if (digits2 == NULL)
+                                         {
+                                           free (digits);
+                                           END_LONG_DOUBLE_ROUNDING ();
+                                           goto out_of_memory;
+                                         }
+                                       if (strlen (digits2) == precision + 1)
+                                         {
+                                           free (digits);
+                                           digits = digits2;
+                                           exponent -= 1;
+                                         }
+                                       else
+                                         free (digits2);
+                                     }
+                                   /* Here ndigits = precision+1.  */
+
                                    *p++ = digits[--ndigits];
                                    if ((flags & FLAG_ALT) || precision > 0)
                                      {
@@ -2968,6 +3027,30 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
                                        adjusted = 1;
                                      }
                                    /* Here ndigits = precision.  */
+                                   if (is_borderline (digits, precision - 1))
+                                     {
+                                       /* Maybe the exponent guess was too high
+                                          and a smaller exponent can be reached
+                                          by turning a 10...0 into 9...9x.  */
+                                       char *digits2 =
+                                         scale10_round_decimal_long_double (arg,
+                                                                            (int)(precision - 1) - exponent + 1);
+                                       if (digits2 == NULL)
+                                         {
+                                           free (digits);
+                                           END_LONG_DOUBLE_ROUNDING ();
+                                           goto out_of_memory;
+                                         }
+                                       if (strlen (digits2) == precision)
+                                         {
+                                           free (digits);
+                                           digits = digits2;
+                                           exponent -= 1;
+                                         }
+                                       else
+                                         free (digits2);
+                                     }
+                                   /* Here ndigits = precision.  */
 
                                    /* Determine the number of trailing zeroes
                                       that have to be dropped.  */
@@ -3062,7 +3145,65 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
                              abort ();
 #  else
                            /* arg is finite.  */
-                           abort ();
+                           if (!(arg == 0.0L))
+                             abort ();
+
+                           pad_ptr = p;
+
+                           if (dp->conversion == 'f' || dp->conversion == 'F')
+                             {
+                               *p++ = '0';
+                               if ((flags & FLAG_ALT) || precision > 0)
+                                 {
+                                   *p++ = decimal_point_char ();
+                                   for (; precision > 0; precision--)
+                                     *p++ = '0';
+                                 }
+                             }
+                           else if (dp->conversion == 'e' || dp->conversion == 'E')
+                             {
+                               *p++ = '0';
+                               if ((flags & FLAG_ALT) || precision > 0)
+                                 {
+                                   *p++ = decimal_point_char ();
+                                   for (; precision > 0; precision--)
+                                     *p++ = '0';
+                                 }
+                               *p++ = dp->conversion; /* 'e' or 'E' */
+                               *p++ = '+';
+                               *p++ = '0';
+                               *p++ = '0';
+                             }
+                           else if (dp->conversion == 'g' || dp->conversion == 'G')
+                             {
+                               *p++ = '0';
+                               if (flags & FLAG_ALT)
+                                 {
+                                   size_t ndigits =
+                                     (precision > 0 ? precision - 1 : 0);
+                                   *p++ = decimal_point_char ();
+                                   for (; ndigits > 0; --ndigits)
+                                     *p++ = '0';
+                                 }
+                             }
+                           else if (dp->conversion == 'a' || dp->conversion == 'A')
+                             {
+                               *p++ = '0';
+                               *p++ = dp->conversion - 'A' + 'X';
+                               pad_ptr = p;
+                               *p++ = '0';
+                               if ((flags & FLAG_ALT) || precision > 0)
+                                 {
+                                   *p++ = decimal_point_char ();
+                                   for (; precision > 0; precision--)
+                                     *p++ = '0';
+                                 }
+                               *p++ = dp->conversion - 'A' + 'P';
+                               *p++ = '+';
+                               *p++ = '0';
+                             }
+                           else
+                             abort ();
 #  endif
                          }
 
@@ -3208,8 +3349,31 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
                                          exponent += 1;
                                        adjusted = 1;
                                      }
-
                                    /* Here ndigits = precision+1.  */
+                                   if (is_borderline (digits, precision))
+                                     {
+                                       /* Maybe the exponent guess was too high
+                                          and a smaller exponent can be reached
+                                          by turning a 10...0 into 9...9x.  */
+                                       char *digits2 =
+                                         scale10_round_decimal_double (arg,
+                                                                       (int)precision - exponent + 1);
+                                       if (digits2 == NULL)
+                                         {
+                                           free (digits);
+                                           goto out_of_memory;
+                                         }
+                                       if (strlen (digits2) == precision + 1)
+                                         {
+                                           free (digits);
+                                           digits = digits2;
+                                           exponent -= 1;
+                                         }
+                                       else
+                                         free (digits2);
+                                     }
+                                   /* Here ndigits = precision+1.  */
+
                                    *p++ = digits[--ndigits];
                                    if ((flags & FLAG_ALT) || precision > 0)
                                      {
@@ -3334,6 +3498,29 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
                                        adjusted = 1;
                                      }
                                    /* Here ndigits = precision.  */
+                                   if (is_borderline (digits, precision - 1))
+                                     {
+                                       /* Maybe the exponent guess was too high
+                                          and a smaller exponent can be reached
+                                          by turning a 10...0 into 9...9x.  */
+                                       char *digits2 =
+                                         scale10_round_decimal_double (arg,
+                                                                       (int)(precision - 1) - exponent + 1);
+                                       if (digits2 == NULL)
+                                         {
+                                           free (digits);
+                                           goto out_of_memory;
+                                         }
+                                       if (strlen (digits2) == precision)
+                                         {
+                                           free (digits);
+                                           digits = digits2;
+                                           exponent -= 1;
+                                         }
+                                       else
+                                         free (digits2);
+                                     }
+                                   /* Here ndigits = precision.  */
 
                                    /* Determine the number of trailing zeroes
                                       that have to be dropped.  */
@@ -3585,7 +3772,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
 #endif
                TCHAR_T *fbp;
                unsigned int prefix_count;
-               int prefixes[2];
+               int prefixes[2] IF_LINT (= { 0 });
 #if !USE_SNPRINTF
                size_t tmp_length;
                TCHAR_T tmpbuf[700];
@@ -4053,7 +4240,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
                      abort ();
                    prefixes[prefix_count++] = a.arg[dp->width_arg_index].a.a_int;
                  }
-               if (dp->precision_arg_index != ARG_NONE)
+               if (!prec_ourselves && dp->precision_arg_index != ARG_NONE)
                  {
                    if (!(a.arg[dp->precision_arg_index].type == TYPE_INT))
                      abort ();