chiark / gitweb /
eglibc (2.11.3-4+deb6u3) squeeze-lts; urgency=medium
[eglibc.git] / stdio-common / printf_size.c
1 /* Print size value using units for orders of magnitude.
2    Copyright (C) 1997-2002, 2004, 2006 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 <ctype.h>
23 #include <ieee754.h>
24 #include <math.h>
25 #include <printf.h>
26 #include <libioP.h>
27 #include <gnu/option-groups.h>
28
29
30 /* This defines make it possible to use the same code for GNU C library and
31    the GNU I/O library.  */
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 \f
42 /* Macros for doing the actual output.  */
43
44 #define outchar(ch)                                                           \
45   do                                                                          \
46     {                                                                         \
47       register const int outc = (ch);                                         \
48       if (putc (outc, fp) == EOF)                                             \
49         return -1;                                                            \
50       ++done;                                                                 \
51     } while (0)
52
53 #define PRINT(ptr, wptr, len)                                                 \
54   do                                                                          \
55     {                                                                         \
56       register size_t outlen = (len);                                         \
57       if (len > 20)                                                           \
58         {                                                                     \
59           if (PUT (fp, wide ? (const char *) wptr : ptr, outlen) != outlen)   \
60             return -1;                                                        \
61           ptr += outlen;                                                      \
62           done += outlen;                                                     \
63         }                                                                     \
64       else                                                                    \
65         {                                                                     \
66           if (wide)                                                           \
67             while (outlen-- > 0)                                              \
68               outchar (*wptr++);                                              \
69           else                                                                \
70             while (outlen-- > 0)                                              \
71               outchar (*ptr++);                                               \
72         }                                                                     \
73     } while (0)
74
75 #define PADN(ch, len)                                                         \
76   do                                                                          \
77     {                                                                         \
78       if (PAD (fp, ch, len) != len)                                           \
79         return -1;                                                            \
80       done += len;                                                            \
81     }                                                                         \
82   while (0)
83 \f
84 /* Prototype for helper functions.  */
85 extern int __printf_fp (FILE *fp, const struct printf_info *info,
86                         const void *const *args);
87
88 \f
89 int
90 __printf_size (FILE *fp, const struct printf_info *info,
91                const void *const *args)
92 {
93   /* Units for the both formats.  */
94 #define BINARY_UNITS    " kmgtpezy"
95 #define DECIMAL_UNITS   " KMGTPEZY"
96   static const char units[2][sizeof (BINARY_UNITS)] =
97   {
98     BINARY_UNITS,       /* For binary format.  */
99     DECIMAL_UNITS       /* For decimal format.  */
100   };
101   const char *tag = units[isupper (info->spec) != 0];
102   int divisor = isupper (info->spec) ? 1000 : 1024;
103
104   /* The floating-point value to output.  */
105   union
106     {
107       union ieee754_double dbl;
108       union ieee854_long_double ldbl;
109     }
110   fpnum;
111   const void *ptr = &fpnum;
112
113   int negative = 0;
114
115   /* "NaN" or "Inf" for the special cases.  */
116   const char *special = NULL;
117   const wchar_t *wspecial = NULL;
118
119   struct printf_info fp_info;
120   int done = 0;
121 #if __OPTION_POSIX_C_LANG_WIDE_CHAR
122   int wide = info->wide;
123 #else
124   /* This should never be called on a wide-oriented stream when
125      OPTION_POSIX_C_LANG_WIDE_CHAR is disabled, but the compiler can't
126      be trusted to figure that out.  */
127   const int wide = 0;
128 #endif
129
130
131   /* Fetch the argument value.  */
132 #ifndef __NO_LONG_DOUBLE_MATH
133   if (info->is_long_double && sizeof (long double) > sizeof (double))
134     {
135       fpnum.ldbl.d = *(const long double *) args[0];
136
137       /* Check for special values: not a number or infinity.  */
138       if (__isnanl (fpnum.ldbl.d))
139         {
140           special = "nan";
141           wspecial = L"nan";
142           negative = 0;
143         }
144       else if (__isinfl (fpnum.ldbl.d))
145         {
146           special = "inf";
147           wspecial = L"inf";
148
149           negative = fpnum.ldbl.d < 0;
150         }
151       else
152         while (fpnum.ldbl.d >= divisor && tag[1] != '\0')
153           {
154             fpnum.ldbl.d /= divisor;
155             ++tag;
156           }
157     }
158   else
159 #endif  /* no long double */
160     {
161       fpnum.dbl.d = *(const double *) args[0];
162
163       /* Check for special values: not a number or infinity.  */
164       if (__isnan (fpnum.dbl.d))
165         {
166           special = "nan";
167           wspecial = L"nan";
168           negative = 0;
169         }
170       else if (__isinf (fpnum.dbl.d))
171         {
172           special = "inf";
173           wspecial = L"inf";
174
175           negative = fpnum.dbl.d < 0;
176         }
177       else
178         while (fpnum.dbl.d >= divisor && tag[1] != '\0')
179           {
180             fpnum.dbl.d /= divisor;
181             ++tag;
182           }
183     }
184
185   if (special)
186     {
187       int width = info->prec > info->width ? info->prec : info->width;
188
189       if (negative || info->showsign || info->space)
190         --width;
191       width -= 3;
192
193       if (!info->left && width > 0)
194         PADN (' ', width);
195
196       if (negative)
197         outchar ('-');
198       else if (info->showsign)
199         outchar ('+');
200       else if (info->space)
201         outchar (' ');
202
203       PRINT (special, wspecial, 3);
204
205       if (info->left && width > 0)
206         PADN (' ', width);
207
208       return done;
209     }
210
211   /* Prepare to print the number.  We want to use `__printf_fp' so we
212      have to prepare a `printf_info' structure.  */
213   fp_info = *info;
214   fp_info.spec = 'f';
215   fp_info.prec = info->prec < 0 ? 3 : info->prec;
216   fp_info.wide = wide;
217
218   if (fp_info.left && fp_info.pad == L' ')
219     {
220       /* We must do the padding ourself since the unit character must
221          be placed before the padding spaces.  */
222       fp_info.width = 0;
223
224       done = __printf_fp (fp, &fp_info, &ptr);
225       if (done > 0)
226         {
227           outchar (*tag);
228           if (info->width > done)
229             PADN (' ', info->width - done);
230         }
231     }
232   else
233     {
234       /* We can let __printf_fp do all the printing and just add our
235          unit character afterwards.  */
236       fp_info.width = info->width - 1;
237
238       done = __printf_fp (fp, &fp_info, &ptr);
239       if (done > 0)
240         outchar (*tag);
241     }
242
243   return done;
244 }
245 ldbl_strong_alias (__printf_size, printf_size);
246 \f
247 /* This is the function used by `vfprintf' to determine number and
248    type of the arguments.  */
249 int
250 printf_size_info (const struct printf_info *info, size_t n, int *argtypes)
251 {
252   /* We need only one double or long double argument.  */
253   if (n >= 1)
254     argtypes[0] = PA_DOUBLE | (info->is_long_double ? PA_FLAG_LONG_DOUBLE : 0);
255
256   return 1;
257 }