Back to index

glibc  2.9
printf_size.c
Go to the documentation of this file.
00001 /* Print size value using units for orders of magnitude.
00002    Copyright (C) 1997-2002, 2004, 2006 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
00005    Based on a proposal by Larry McVoy <lm@sgi.com>.
00006 
00007    The GNU C Library is free software; you can redistribute it and/or
00008    modify it under the terms of the GNU Lesser General Public
00009    License as published by the Free Software Foundation; either
00010    version 2.1 of the License, or (at your option) any later version.
00011 
00012    The GNU C Library is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015    Lesser General Public License for more details.
00016 
00017    You should have received a copy of the GNU Lesser General Public
00018    License along with the GNU C Library; if not, write to the Free
00019    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00020    02111-1307 USA.  */
00021 
00022 #include <ctype.h>
00023 #include <ieee754.h>
00024 #include <math.h>
00025 #include <printf.h>
00026 #include <libioP.h>
00027 
00028 
00029 /* This defines make it possible to use the same code for GNU C library and
00030    the GNU I/O library.      */
00031 #define PUT(f, s, n) _IO_sputn (f, s, n)
00032 #define PAD(f, c, n) (wide ? _IO_wpadn (f, c, n) : INTUSE(_IO_padn) (f, c, n))
00033 /* We use this file GNU C library and GNU I/O library.  So make
00034    names equal.       */
00035 #undef putc
00036 #define putc(c, f) (wide \
00037                   ? (int)_IO_putwc_unlocked (c, f) : _IO_putc_unlocked (c, f))
00038 #define size_t       _IO_size_t
00039 #define FILE  _IO_FILE
00040 
00041 /* Macros for doing the actual output.  */
00042 
00043 #define outchar(ch)                                                  \
00044   do                                                                 \
00045     {                                                                \
00046       register const int outc = (ch);                                       \
00047       if (putc (outc, fp) == EOF)                                    \
00048        return -1;                                                    \
00049       ++done;                                                        \
00050     } while (0)
00051 
00052 #define PRINT(ptr, wptr, len)                                               \
00053   do                                                                 \
00054     {                                                                \
00055       register size_t outlen = (len);                                       \
00056       if (len > 20)                                                  \
00057        {                                                             \
00058          if (PUT (fp, wide ? (const char *) wptr : ptr, outlen) != outlen)   \
00059            return -1;                                                       \
00060          ptr += outlen;                                              \
00061          done += outlen;                                             \
00062        }                                                             \
00063       else                                                           \
00064        {                                                             \
00065          if (wide)                                                   \
00066            while (outlen-- > 0)                                      \
00067              outchar (*wptr++);                                      \
00068          else                                                        \
00069            while (outlen-- > 0)                                      \
00070              outchar (*ptr++);                                              \
00071        }                                                             \
00072     } while (0)
00073 
00074 #define PADN(ch, len)                                                       \
00075   do                                                                 \
00076     {                                                                \
00077       if (PAD (fp, ch, len) != len)                                         \
00078        return -1;                                                    \
00079       done += len;                                                   \
00080     }                                                                \
00081   while (0)
00082 
00083 /* Prototype for helper functions.  */
00084 extern int __printf_fp (FILE *fp, const struct printf_info *info,
00085                      const void *const *args);
00086 
00087 
00088 int
00089 __printf_size (FILE *fp, const struct printf_info *info,
00090               const void *const *args)
00091 {
00092   /* Units for the both formats.  */
00093 #define BINARY_UNITS " kmgtpezy"
00094 #define DECIMAL_UNITS       " KMGTPEZY"
00095   static const char units[2][sizeof (BINARY_UNITS)] =
00096   {
00097     BINARY_UNITS,    /* For binary format.  */
00098     DECIMAL_UNITS    /* For decimal format.  */
00099   };
00100   const char *tag = units[isupper (info->spec) != 0];
00101   int divisor = isupper (info->spec) ? 1000 : 1024;
00102 
00103   /* The floating-point value to output.  */
00104   union
00105     {
00106       union ieee754_double dbl;
00107       union ieee854_long_double ldbl;
00108     }
00109   fpnum;
00110   const void *ptr = &fpnum;
00111 
00112   int negative = 0;
00113 
00114   /* "NaN" or "Inf" for the special cases.  */
00115   const char *special = NULL;
00116   const wchar_t *wspecial = NULL;
00117 
00118   struct printf_info fp_info;
00119   int done = 0;
00120   int wide = info->wide;
00121 
00122 
00123   /* Fetch the argument value.     */
00124 #ifndef __NO_LONG_DOUBLE_MATH
00125   if (info->is_long_double && sizeof (long double) > sizeof (double))
00126     {
00127       fpnum.ldbl.d = *(const long double *) args[0];
00128 
00129       /* Check for special values: not a number or infinity.  */
00130       if (__isnanl (fpnum.ldbl.d))
00131        {
00132          special = "nan";
00133          wspecial = L"nan";
00134          negative = 0;
00135        }
00136       else if (__isinfl (fpnum.ldbl.d))
00137        {
00138          special = "inf";
00139          wspecial = L"inf";
00140 
00141          negative = fpnum.ldbl.d < 0;
00142        }
00143       else
00144        while (fpnum.ldbl.d >= divisor && tag[1] != '\0')
00145          {
00146            fpnum.ldbl.d /= divisor;
00147            ++tag;
00148          }
00149     }
00150   else
00151 #endif /* no long double */
00152     {
00153       fpnum.dbl.d = *(const double *) args[0];
00154 
00155       /* Check for special values: not a number or infinity.  */
00156       if (__isnan (fpnum.dbl.d))
00157        {
00158          special = "nan";
00159          wspecial = L"nan";
00160          negative = 0;
00161        }
00162       else if (__isinf (fpnum.dbl.d))
00163        {
00164          special = "inf";
00165          wspecial = L"inf";
00166 
00167          negative = fpnum.dbl.d < 0;
00168        }
00169       else
00170        while (fpnum.dbl.d >= divisor && tag[1] != '\0')
00171          {
00172            fpnum.dbl.d /= divisor;
00173            ++tag;
00174          }
00175     }
00176 
00177   if (special)
00178     {
00179       int width = info->prec > info->width ? info->prec : info->width;
00180 
00181       if (negative || info->showsign || info->space)
00182        --width;
00183       width -= 3;
00184 
00185       if (!info->left && width > 0)
00186        PADN (' ', width);
00187 
00188       if (negative)
00189        outchar ('-');
00190       else if (info->showsign)
00191        outchar ('+');
00192       else if (info->space)
00193        outchar (' ');
00194 
00195       PRINT (special, wspecial, 3);
00196 
00197       if (info->left && width > 0)
00198        PADN (' ', width);
00199 
00200       return done;
00201     }
00202 
00203   /* Prepare to print the number.  We want to use `__printf_fp' so we
00204      have to prepare a `printf_info' structure.  */
00205   fp_info = *info;
00206   fp_info.spec = 'f';
00207   fp_info.prec = info->prec < 0 ? 3 : info->prec;
00208   fp_info.wide = wide;
00209 
00210   if (fp_info.left && fp_info.pad == L' ')
00211     {
00212       /* We must do the padding ourself since the unit character must
00213         be placed before the padding spaces.  */
00214       fp_info.width = 0;
00215 
00216       done = __printf_fp (fp, &fp_info, &ptr);
00217       if (done > 0)
00218        {
00219          outchar (*tag);
00220          if (info->width > done)
00221            PADN (' ', info->width - done);
00222        }
00223     }
00224   else
00225     {
00226       /* We can let __printf_fp do all the printing and just add our
00227         unit character afterwards.  */
00228       fp_info.width = info->width - 1;
00229 
00230       done = __printf_fp (fp, &fp_info, &ptr);
00231       if (done > 0)
00232        outchar (*tag);
00233     }
00234 
00235   return done;
00236 }
00237 ldbl_strong_alias (__printf_size, printf_size);
00238 
00239 /* This is the function used by `vfprintf' to determine number and
00240    type of the arguments.  */
00241 int
00242 printf_size_info (const struct printf_info *info, size_t n, int *argtypes)
00243 {
00244   /* We need only one double or long double argument.  */
00245   if (n >= 1)
00246     argtypes[0] = PA_DOUBLE | (info->is_long_double ? PA_FLAG_LONG_DOUBLE : 0);
00247 
00248   return 1;
00249 }