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