Bash-4.3 distribution sources and documentation
[platform/upstream/bash.git] / lib / sh / snprintf.c
index d46b2d9..87ca217 100644 (file)
@@ -9,7 +9,7 @@
    Unix snprintf implementation.
    derived from inetutils/libinetutils/snprintf.c Version 1.1
 
-   Copyright (C) 2001,2006,2010 Free Software Foundation, Inc.
+   Copyright (C) 2001,2006,2010,2012 Free Software Foundation, Inc.
 
    This file is part of GNU Bash, the Bourne Again SHell.
 
@@ -26,7 +26,7 @@
    You should have received a copy of the GNU General Public License
    along with Bash.  If not, see <http://www.gnu.org/licenses/>.
    
-   Revision History:
+   Original (pre-bash) Revision History:
 
    1.1:
       *  added changes from Miles Bader
@@ -50,7 +50,6 @@
  * Currently doesn't handle (and bash/readline doesn't use):
  *     * *M$ width, precision specifications
  *     * %N$ numbered argument conversions
- *     * inf, nan floating values imperfect (if isinf(), isnan() not in libc)
  *     * support for `F' is imperfect with ldfallback(), since underlying
  *       printf may not handle it -- should ideally have another autoconf test
  */
@@ -303,11 +302,30 @@ static void dfallback __P((struct DATA *, const char *, const char *, double));
 
 static char *groupnum __P((char *));
 
-#ifndef HAVE_ISINF_IN_LIBC
-static int isinf __P((double));
+#if defined (HAVE_LONG_DOUBLE)
+#  define LONGDOUBLE long double
+#else
+#  define LONGDOUBLE double
+#endif
+
+#ifndef isnan
+  static inline int isnan_f  (float       x) { return x != x; }
+  static inline int isnan_d  (double      x) { return x != x; }
+  static inline int isnan_ld (LONGDOUBLE  x) { return x != x; }
+  # define isnan(x) \
+      (sizeof (x) == sizeof (LONGDOUBLE) ? isnan_ld (x) \
+       : sizeof (x) == sizeof (double) ? isnan_d (x) \
+       : isnan_f (x))
 #endif
-#ifndef HAVE_ISNAN_IN_LIBC
-static int isnan __P((double));
+  
+#ifndef isinf
+  static inline int isinf_f  (float       x) { return !isnan (x) && isnan (x - x); }
+  static inline int isinf_d  (double      x) { return !isnan (x) && isnan (x - x); }
+  static inline int isinf_ld (LONGDOUBLE  x) { return !isnan (x) && isnan (x - x); }
+  # define isinf(x) \
+      (sizeof (x) == sizeof (LONGDOUBLE) ? isinf_ld (x) \
+       : sizeof (x) == sizeof (double) ? isinf_d (x) \
+       : isinf_f (x))
 #endif
 
 #ifdef DRIVER
@@ -371,7 +389,7 @@ static void xfree __P((void *));
        while (0)
 
 #define PUT_PLUS(d, p, zero) \
-           if ((d) > zero && (p)->justify == RIGHT) \
+           if (((p)->flags & PF_PLUS) && (d) > zero) \
              PUT_CHAR('+', p)
 
 #define PUT_SPACE(d, p, zero) \
@@ -431,9 +449,9 @@ static void xfree __P((void *));
          if (lv) \
            { \
              if (lv->decimal_point && lv->decimal_point[0]) \
-               (d) = lv->decimal_point[0]; \
+               (d) = lv->decimal_point[0]; \
              if (lv->thousands_sep && lv->thousands_sep[0]) \
-               (t) = lv->thousands_sep[0]; \
+               (t) = lv->thousands_sep[0]; \
              (g) = lv->grouping ? lv->grouping : ""; \
              if (*(g) == '\0' || *(g) == CHAR_MAX || (t) == -1) (g) = 0; \
            } \
@@ -574,7 +592,7 @@ integral(real, ip)
 /* 
  * return an ascii representation of the integral part of the number
  * and set fract to be an ascii representation of the fraction part
- * the container for the fraction and the integral part or staticly
+ * the container for the fraction and the integral part or statically
  * declare with fix size 
  */
 static char *
@@ -586,10 +604,9 @@ numtoa(number, base, precision, fract)
   register int i, j;
   double ip, fp; /* integer and fraction part */
   double fraction;
-  int digits = MAX_INT - 1;
+  int digits, sign;
   static char integral_part[MAX_INT];
   static char fraction_part[MAX_FRACT];
-  double sign;
   int ch;
 
   /* taking care of the obvious case: 0.0 */
@@ -607,8 +624,12 @@ numtoa(number, base, precision, fract)
       return integral_part;
     }
 
+  /* -0 is tricky */
+  sign = (number == -0.) ? '-' : ((number < 0.) ? '-' : '+');
+  digits = MAX_INT - 1;
+
   /* for negative numbers */
-  if ((sign = number) < 0.)
+  if (sign == '-')
     {
       number = -number;
       digits--; /* sign consume one digit */
@@ -643,7 +664,7 @@ numtoa(number, base, precision, fract)
       integral_part[i] = '9';
 
   /* put the sign ? */
-  if (sign < 0.)
+  if (sign == '-')
     integral_part[i++] = '-';
 
   integral_part[i] = '\0';
@@ -682,9 +703,13 @@ number(p, d, base)
   long sd;
   int flags;
 
-  /* An explicit precision turns off the zero-padding flag. */
+  /* An explicit precision turns off the zero-padding flag and sets the
+     pad character back to space. */
   if ((p->flags & PF_ZEROPAD) && p->precision >= 0 && (p->flags & PF_DOT))
-    p->flags &= ~PF_ZEROPAD;
+    {
+      p->flags &= ~PF_ZEROPAD;
+      p->pad = ' ';
+    }
 
   sd = d;      /* signed for ' ' padding in base 10 */
   flags = 0;
@@ -698,10 +723,11 @@ number(p, d, base)
     {
       GETLOCALEDATA(decpoint, thoussep, grouping);
       if (grouping && (t = groupnum (tmp)))
-        tmp = t;
+       tmp = t;
     }
 
-  p->width -= strlen(tmp);
+  /* need to add one for any `+', but we only add one in base 10 */
+  p->width -= strlen(tmp) + (base == 10 && d > 0 && (p->flags & PF_PLUS));
   PAD_RIGHT(p);
 
   if ((p->flags & PF_DOT) && p->precision > 0)
@@ -753,9 +779,13 @@ lnumber(p, d, base)
   long long sd;
   int flags;
 
-  /* An explicit precision turns off the zero-padding flag. */
+  /* An explicit precision turns off the zero-padding flag and sets the
+     pad character back to space. */
   if ((p->flags & PF_ZEROPAD) && p->precision >= 0 && (p->flags & PF_DOT))
-    p->flags &= ~PF_ZEROPAD;
+    {
+      p->flags &= ~PF_ZEROPAD;
+      p->pad = ' ';
+    }
 
   sd = d;      /* signed for ' ' padding in base 10 */
   flags = (*p->pf == 'x' || *p->pf == 'X' || *p->pf == 'o' || *p->pf == 'u' || *p->pf == 'U') ? FL_UNSIGNED : 0;
@@ -768,10 +798,11 @@ lnumber(p, d, base)
     {
       GETLOCALEDATA(decpoint, thoussep, grouping);
       if (grouping && (t = groupnum (tmp)))
-        tmp = t;
+       tmp = t;
     }
 
-  p->width -= strlen(tmp);
+  /* need to add one for any `+', but we only add one in base 10 */
+  p->width -= strlen(tmp) + (base == 10 && d > 0 && (p->flags & PF_PLUS));
   PAD_RIGHT(p);
 
   if ((p->flags & PF_DOT) && p->precision > 0)
@@ -875,11 +906,11 @@ wstrings(p, tmp)
     {
       len = wcsrtombs (NULL, &ws, 0, &mbs);
       if (len != (size_t)-1)
-        {
+       {
          memset (&mbs, '\0', sizeof (mbstate_t));
          os = (char *)xmalloc (len + 1);
          (void)wcsrtombs (os, &ws, len + 1, &mbs);
-        }
+       }
     }
   if (len == (size_t)-1)
     {
@@ -919,32 +950,6 @@ wchars (p, wc)
 
 #ifdef FLOATING_POINT
 
-#ifndef HAVE_ISINF_IN_LIBC
-/* Half-assed versions, since we don't want to link with libm. */
-static int
-isinf(d)
-     double d;
-{
-#ifdef DBL_MAX
-  if (d < DBL_MIN)
-    return -1;
-  else if (d > DBL_MAX)
-    return 1;
-  else
-#endif
-    return 0;
-}
-#endif
-
-#ifndef HAVE_ISNAN_IN_LIBC
-static int
-isnan(d)
-     double d;
-{
-  return 0;
-}
-#endif
-
 /* Check for [+-]infinity and NaN.  If MODE == 1, we check for Infinity, else
    (mode == 2) we check for NaN.  This does the necessary printing.  Returns
    1 if Inf or Nan, 0 if not. */
@@ -1002,19 +1007,35 @@ floating(p, d)
     {
       /* smash the trailing zeros unless altform */
       for (i = strlen(tmp2) - 1; i >= 0 && tmp2[i] == '0'; i--)
-        tmp2[i] = '\0'; 
+       tmp2[i] = '\0'; 
       if (tmp2[0] == '\0')
        p->precision = 0;
     }
 
   /* calculate the padding. 1 for the dot */
   p->width = p->width -
+           /* XXX - should this be d>0. && (p->flags & PF_PLUS) ? */
+#if 0
            ((d > 0. && p->justify == RIGHT) ? 1:0) -
+#else
+           ((d > 0. && (p->flags & PF_PLUS)) ? 1:0) -
+#endif
            ((p->flags & PF_SPACE) ? 1:0) -
            strlen(tmp) - p->precision -
            ((p->precision != 0 || (p->flags & PF_ALTFORM)) ? 1 : 0);   /* radix char */
-  PAD_RIGHT(p);  
-  PUT_PLUS(d, p, 0.);
+
+  if (p->pad == ' ')
+    {
+      PAD_RIGHT(p);
+      PUT_PLUS(d, p, 0.);
+    }
+  else
+    {
+      if (*tmp == '-')
+       PUT_CHAR(*tmp++, p);
+      PUT_PLUS(d, p, 0.);
+      PAD_RIGHT(p);
+    }
   PUT_SPACE(d, p, 0.);
 
   while (*tmp)
@@ -1058,14 +1079,30 @@ exponent(p, d)
   tmp = dtoa(d, p->precision, &tmp2);
 
   /* 1 for unit, 1 for the '.', 1 for 'e|E',
-   * 1 for '+|-', 2 for 'exp' */
+   * 1 for '+|-', 2 for 'exp'  (but no `.' if precision == 0 */
   /* calculate how much padding need */
   p->width = p->width - 
+           /* XXX - should this be d>0. && (p->flags & PF_PLUS) ? */
+#if 0
             ((d > 0. && p->justify == RIGHT) ? 1:0) -
-            ((p->flags & PF_SPACE) ? 1:0) - p->precision - 6;
+#else
+            ((d > 0. && (p->flags & PF_PLUS)) ? 1:0) -
+#endif
+            (p->precision != 0 || (p->flags & PF_ALTFORM)) -
+            ((p->flags & PF_SPACE) ? 1:0) - p->precision - 5;
 
-  PAD_RIGHT(p);
-  PUT_PLUS(d, p, 0.);
+  if (p->pad == ' ')
+    {
+      PAD_RIGHT(p);
+      PUT_PLUS(d, p, 0.);
+    }
+  else
+    {
+      if (*tmp == '-')
+       PUT_CHAR(*tmp++, p);
+      PUT_PLUS(d, p, 0.);
+      PAD_RIGHT(p);
+    }
   PUT_SPACE(d, p, 0.);
 
   while (*tmp)
@@ -1163,7 +1200,7 @@ groupnum (s)
          else if (*g == CHAR_MAX)
            {
              do
-               *--re = *--se;
+               *--re = *--se;
              while (se > s);
              break;
            }
@@ -1295,10 +1332,6 @@ vsnprintf_internal(data, string, length, format, args)
              case '#':
                data->flags |= PF_ALTFORM;
                continue;
-             case '0':
-               data->flags |= PF_ZEROPAD;
-               data->pad = '0';
-               continue;
              case '*':
                if (data->flags & PF_DOT)
                  data->flags |= PF_STAR_P;
@@ -1322,13 +1355,25 @@ vsnprintf_internal(data, string, length, format, args)
                if ((data->flags & PF_DOT) == 0)
                  {
                    data->flags |= PF_PLUS;
-                   data->justify = RIGHT;
+                   if ((data->flags & PF_LADJUST) == 0)
+                     data->justify = RIGHT;
                  }
                continue;
              case '\'':
                data->flags |= PF_THOUSANDS;
                continue;
 
+             case '0':
+               /* If we're not specifying precision (in which case we've seen
+                  a `.') and we're not performing left-adjustment (in which
+                  case the `0' is ignored), a `0' is taken as the zero-padding
+                  flag. */
+               if ((data->flags & (PF_DOT|PF_LADJUST)) == 0)
+                 {
+                   data->flags |= PF_ZEROPAD;
+                   data->pad = '0';
+                   continue;
+                 }
              case '1': case '2': case '3':
              case '4': case '5': case '6':
              case '7': case '8': case '9':
@@ -1410,8 +1455,9 @@ conv_break:
                else
                  {
                    /* reduce precision by 1 because of leading digit before
-                      decimal point in e format. */
-                   data->precision--;
+                      decimal point in e format, unless specified as 0. */
+                   if (data->precision > 0)
+                     data->precision--;
                    exponent(data, d);
                  }
                state = 0;