(root)/
glibc-2.38/
stdio-common/
printf_size.c
       1  /* Print size value using units for orders of magnitude.
       2     Copyright (C) 1997-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <ctype.h>
      20  #include <ieee754.h>
      21  #include <math.h>
      22  #include <printf.h>
      23  #include <libioP.h>
      24  
      25  #define PUT(f, s, n) _IO_sputn (f, s, n)
      26  #define PAD(f, c, n) (wide ? _IO_wpadn (f, c, n) : _IO_padn (f, c, n))
      27  #undef putc
      28  #define putc(c, f) (wide \
      29  		    ? (int)_IO_putwc_unlocked (c, f) : _IO_putc_unlocked (c, f))
      30  
      31  
      32  /* Macros for doing the actual output.  */
      33  
      34  #define outchar(ch)							      \
      35    do									      \
      36      {									      \
      37        const int outc = (ch);						      \
      38        if (putc (outc, fp) == EOF)					      \
      39  	return -1;							      \
      40        ++done;								      \
      41      } while (0)
      42  
      43  #define PRINT(ptr, wptr, len)						      \
      44    do									      \
      45      {									      \
      46        size_t outlen = (len);						      \
      47        if (len > 20)							      \
      48  	{								      \
      49  	  if (PUT (fp, wide ? (const char *) wptr : ptr, outlen) != outlen)   \
      50  	    return -1;							      \
      51  	  ptr += outlen;						      \
      52  	  done += outlen;						      \
      53  	}								      \
      54        else								      \
      55  	{								      \
      56  	  if (wide)							      \
      57  	    while (outlen-- > 0)					      \
      58  	      outchar (*wptr++);					      \
      59  	  else								      \
      60  	    while (outlen-- > 0)					      \
      61  	      outchar (*ptr++);						      \
      62  	}								      \
      63      } while (0)
      64  
      65  #define PADN(ch, len)							      \
      66    do									      \
      67      {									      \
      68        if (PAD (fp, ch, len) != len)					      \
      69  	return -1;							      \
      70        done += len;							      \
      71      }									      \
      72    while (0)
      73  
      74  /* Prototype for helper functions.  */
      75  extern int __printf_fp (FILE *fp, const struct printf_info *info,
      76  			const void *const *args);
      77  
      78  
      79  int
      80  __printf_size (FILE *fp, const struct printf_info *info,
      81  	       const void *const *args)
      82  {
      83    /* Units for the both formats.  */
      84  #define BINARY_UNITS	" kmgtpezy"
      85  #define DECIMAL_UNITS	" KMGTPEZY"
      86    static const char units[2][sizeof (BINARY_UNITS)] =
      87    {
      88      BINARY_UNITS,	/* For binary format.  */
      89      DECIMAL_UNITS	/* For decimal format.  */
      90    };
      91    const char *tag = units[isupper (info->spec) != 0];
      92    int divisor = isupper (info->spec) ? 1000 : 1024;
      93  
      94    /* The floating-point value to output.  */
      95    union
      96      {
      97        union ieee754_double dbl;
      98        long double ldbl;
      99  #if __HAVE_DISTINCT_FLOAT128
     100        _Float128 f128;
     101  #endif
     102      }
     103    fpnum;
     104    const void *ptr = &fpnum;
     105  
     106    int is_neg = 0;
     107  
     108    /* "NaN" or "Inf" for the special cases.  */
     109    const char *special = NULL;
     110    const wchar_t *wspecial = NULL;
     111  
     112    struct printf_info fp_info;
     113    int done = 0;
     114    int wide = info->wide;
     115  
     116  #define PRINTF_SIZE_FETCH(FLOAT, VAR)					\
     117    {									\
     118      (VAR) = *(const FLOAT *) args[0];					\
     119  									\
     120      /* Check for special values: not a number or infinity.  */		\
     121      if (isnan (VAR))							\
     122        {									\
     123  	special = "nan";						\
     124  	wspecial = L"nan";						\
     125  	/* is_neg = 0; Already zero */					\
     126        }									\
     127      else if (isinf (VAR))						\
     128        {									\
     129  	is_neg = signbit (VAR);						\
     130  	special = "inf";						\
     131  	wspecial = L"inf";						\
     132        }									\
     133      else								\
     134        while ((VAR) >= divisor && tag[1] != '\0')			\
     135  	{								\
     136  	  (VAR) /= divisor;						\
     137  	  ++tag;							\
     138  	}								\
     139    }
     140  
     141    /* Fetch the argument value.	*/
     142  #if __HAVE_DISTINCT_FLOAT128
     143    if (info->is_binary128)
     144      PRINTF_SIZE_FETCH (_Float128, fpnum.f128)
     145    else
     146  #endif
     147  #ifndef __NO_LONG_DOUBLE_MATH
     148    if (info->is_long_double && sizeof (long double) > sizeof (double))
     149      PRINTF_SIZE_FETCH (long double, fpnum.ldbl)
     150    else
     151  #endif
     152      PRINTF_SIZE_FETCH (double, fpnum.dbl.d)
     153  
     154  #undef PRINTF_SIZE_FETCH
     155  
     156    if (special)
     157      {
     158        int width = info->prec > info->width ? info->prec : info->width;
     159  
     160        if (is_neg || info->showsign || info->space)
     161  	--width;
     162        width -= 3;
     163  
     164        if (!info->left && width > 0)
     165  	PADN (' ', width);
     166  
     167        if (is_neg)
     168  	outchar ('-');
     169        else if (info->showsign)
     170  	outchar ('+');
     171        else if (info->space)
     172  	outchar (' ');
     173  
     174        PRINT (special, wspecial, 3);
     175  
     176        if (info->left && width > 0)
     177  	PADN (' ', width);
     178  
     179        return done;
     180      }
     181  
     182    /* Prepare to print the number.  We want to use `__printf_fp' so we
     183       have to prepare a `printf_info' structure.  */
     184    fp_info = *info;
     185    fp_info.spec = 'f';
     186    fp_info.prec = info->prec < 0 ? 3 : info->prec;
     187    fp_info.wide = wide;
     188  
     189    if (fp_info.left && fp_info.pad == L' ')
     190      {
     191        /* We must do the padding ourself since the unit character must
     192  	 be placed before the padding spaces.  */
     193        fp_info.width = 0;
     194  
     195        done = __printf_fp (fp, &fp_info, &ptr);
     196        if (done > 0)
     197  	{
     198  	  outchar (*tag);
     199  	  if (info->width > done)
     200  	    PADN (' ', info->width - done);
     201  	}
     202      }
     203    else
     204      {
     205        /* We can let __printf_fp do all the printing and just add our
     206  	 unit character afterwards.  */
     207        fp_info.width = info->width - 1;
     208  
     209        done = __printf_fp (fp, &fp_info, &ptr);
     210        if (done > 0)
     211  	outchar (*tag);
     212      }
     213  
     214    return done;
     215  }
     216  ldbl_strong_alias (__printf_size, printf_size);
     217  
     218  /* This is the function used by `vfprintf' to determine number and
     219     type of the arguments.  */
     220  int
     221  printf_size_info (const struct printf_info *info, size_t n, int *argtypes)
     222  {
     223    /* We need only one double or long double argument.  */
     224    if (n >= 1)
     225      argtypes[0] = PA_DOUBLE | (info->is_long_double ? PA_FLAG_LONG_DOUBLE : 0);
     226  
     227    return 1;
     228  }