1 /* Print size value using units for orders of magnitude. 2 Copyright (C) 1997,1998,1999,2000,2001,2002 Free Software Foundation, Inc. 3 This file is part of the GNU C Library. 4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997. 5 Based on a proposal by Larry McVoy <lm@sgi.com>. 6 7 The GNU C Library is free software; you can redistribute it and/or 8 modify it under the terms of the GNU Lesser General Public 9 License as published by the Free Software Foundation; either 10 version 2.1 of the License, or (at your option) any later version. 11 12 The GNU C Library is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 Lesser General Public License for more details. 16 17 You should have received a copy of the GNU Lesser General Public 18 License along with the GNU C Library; if not, write to the Free 19 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 20 02111-1307 USA. */ 21 22 #include <stdio_private.h> 23 #include <ctype.h> 24 #include <ieee754.h> 25 #include <math.h> 26 #include <printf.h> 27 28 29 /* This defines make it possible to use the same code for GNU C library and 30 the GNU I/O library. */ 31 #ifdef USE_IN_LIBIO 32 # define PUT(f, s, n) _IO_sputn (f, s, n) 33 # define PAD(f, c, n) (wide ? _IO_wpadn (f, c, n) : INTUSE(_IO_padn) (f, c, n)) 34 /* We use this file GNU C library and GNU I/O library. So make 35 names equal. */ 36 # undef putc 37 # define putc(c, f) (wide \ 38 ? (int)_IO_putwc_unlocked (c, f) : _IO_putc_unlocked (c, f)) 39 # define size_t _IO_size_t 40 # define FILE _IO_FILE 41 #else /* ! USE_IN_LIBIO */ 42 # define PUT(f, s, n) fwrite (s, 1, n, f) 43 # define PAD(f, c, n) __printf_pad (f, c, n) 44 ssize_t __printf_pad __P ((FILE *, char pad, int n)); /* In vfprintf.c. */ 45 #endif /* USE_IN_LIBIO */ 46 47 /* Macros for doing the actual output. */ 48 49 #define outchar(ch) \ 50 do \ 51 { \ 52 register const int outc = (ch); \ 53 if (putc (outc, fp) == EOF) \ 54 return -1; \ 55 ++done; \ 56 } while (0) 57 58 #define PRINT(ptr, wptr, len) \ 59 do \ 60 { \ 61 register size_t outlen = (len); \ 62 if (len > 20) \ 63 { \ 64 if (PUT (fp, wide ? (const char *) wptr : ptr, outlen) != outlen) \ 65 return -1; \ 66 ptr += outlen; \ 67 done += outlen; \ 68 } \ 69 else \ 70 { \ 71 if (wide) \ 72 while (outlen-- > 0) \ 73 outchar (*wptr++); \ 74 else \ 75 while (outlen-- > 0) \ 76 outchar (*ptr++); \ 77 } \ 78 } while (0) 79 80 #define PADN(ch, len) \ 81 do \ 82 { \ 83 if (PAD (fp, ch, len) != len) \ 84 return -1; \ 85 done += len; \ 86 } \ 87 while (0) 88 89 /* Prototype for helper functions. */ 90 extern int __printf_fp (FILE *fp, const struct printf_info *info, 91 const void *const *args); 92 93 94 int 95 printf_size (FILE *fp, const struct printf_info *info, const void *const *args) 96 { 97 /* Units for the both formats. */ 98 #define BINARY_UNITS " kmgtpezy" 99 #define DECIMAL_UNITS " KMGTPEZY" 100 static const char units[2][sizeof (BINARY_UNITS)] = 101 { 102 BINARY_UNITS, /* For binary format. */ 103 DECIMAL_UNITS /* For decimal format. */ 104 }; 105 const char *tag = units[isupper (info->spec) != 0]; 106 int divisor = isupper (info->spec) ? 1000 : 1024; 107 108 /* The floating-point value to output. */ 109 union 110 { 111 union ieee754_double dbl; 112 union ieee854_long_double ldbl; 113 } 114 fpnum; 115 const void *ptr = &fpnum; 116 117 int negative = 0; 118 119 /* "NaN" or "Inf" for the special cases. */ 120 const char *special = NULL; 121 const wchar_t *wspecial = NULL; 122 123 struct printf_info fp_info; 124 int done = 0; 125 int wide = info->wide; 126 127 128 /* Fetch the argument value. */ 129 #ifndef __NO_LONG_DOUBLE_MATH 130 if (info->is_long_double && sizeof (long double) > sizeof (double)) 131 { 132 fpnum.ldbl.d = *(const long double *) args[0]; 133 134 /* Check for special values: not a number or infinity. */ 135 if (__isnanl (fpnum.ldbl.d)) 136 { 137 special = "nan"; 138 wspecial = L"nan"; 139 negative = 0; 140 } 141 else if (__isinfl (fpnum.ldbl.d)) 142 { 143 special = "inf"; 144 wspecial = L"inf"; 145 146 negative = fpnum.ldbl.d < 0; 147 } 148 else 149 while (fpnum.ldbl.d >= divisor && tag[1] != '\0') 150 { 151 fpnum.ldbl.d /= divisor; 152 ++tag; 153 } 154 } 155 else 156 #endif /* no long double */ 157 { 158 fpnum.dbl.d = *(const double *) args[0]; 159 160 /* Check for special values: not a number or infinity. */ 161 if (__isnan (fpnum.dbl.d)) 162 { 163 special = "nan"; 164 wspecial = L"nan"; 165 negative = 0; 166 } 167 else if (__isinf (fpnum.dbl.d)) 168 { 169 special = "inf"; 170 wspecial = L"inf"; 171 172 negative = fpnum.dbl.d < 0; 173 } 174 else 175 while (fpnum.dbl.d >= divisor && tag[1] != '\0') 176 { 177 fpnum.dbl.d /= divisor; 178 ++tag; 179 } 180 } 181 182 if (special) 183 { 184 int width = info->prec > width ? info->prec : width; 185 186 if (negative || info->showsign || info->space) 187 --width; 188 width -= 3; 189 190 if (!info->left && width > 0) 191 PADN (' ', width); 192 193 if (negative) 194 outchar ('-'); 195 else if (info->showsign) 196 outchar ('+'); 197 else if (info->space) 198 outchar (' '); 199 200 PRINT (special, wspecial, 3); 201 202 if (info->left && width > 0) 203 PADN (' ', width); 204 205 return done; 206 } 207 208 /* Prepare to print the number. We want to use `__printf_fp' so we 209 have to prepare a `printf_info' structure. */ 210 fp_info.spec = 'f'; 211 fp_info.prec = info->prec < 0 ? 3 : info->prec; 212 fp_info.is_long_double = info->is_long_double; 213 fp_info.is_short = info->is_short; 214 fp_info.is_long = info->is_long; 215 fp_info.alt = info->alt; 216 fp_info.space = info->space; 217 fp_info.left = info->left; 218 fp_info.showsign = info->showsign; 219 fp_info.group = info->group; 220 fp_info.extra = info->extra; 221 fp_info.pad = info->pad; 222 fp_info.wide = wide; 223 224 if (fp_info.left && fp_info.pad == L' ') 225 { 226 /* We must do the padding ourself since the unit character must 227 be placed before the padding spaces. */ 228 fp_info.width = 0; 229 230 done = __printf_fp (fp, &fp_info, &ptr); 231 if (done > 0) 232 { 233 outchar (*tag); 234 if (info->width > done) 235 PADN (' ', info->width - done); 236 } 237 } 238 else 239 { 240 /* We can let __printf_fp do all the printing and just add our 241 unit character afterwards. */ 242 fp_info.width = info->width - 1; 243 244 done = __printf_fp (fp, &fp_info, &ptr); 245 if (done > 0) 246 outchar (*tag); 247 } 248 249 return done; 250 } 251 252 /* This is the function used by `vfprintf' to determine number and 253 type of the arguments. */ 254 int 255 printf_size_info (const struct printf_info *info, size_t n, int *argtypes) 256 { 257 /* We need only one double or long double argument. */ 258 if (n >= 1) 259 argtypes[0] = PA_DOUBLE | (info->is_long_double ? PA_FLAG_LONG_DOUBLE : 0); 260 261 return 1; 262 } 263