OSDN Git Service

Daily bump.
[pf3gnuchains/gcc-fork.git] / libquadmath / printf / quadmath-printf.c
1 /* GCC Quad-Precision Math Library
2    Copyright (C) 2011 Free Software Foundation, Inc.
3    Written by Jakub Jelinek  <jakub@redhat.com>
4
5 This file is part of the libquadmath library.
6 Libquadmath is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 Libquadmath is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public
17 License along with libquadmath; see the file COPYING.LIB.  If
18 not, write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
19 Boston, MA 02110-1301, USA.  */
20
21 #include <config.h>
22 #include <stdarg.h>
23 #include <string.h>
24 #include <stdio.h>
25 #include "quadmath-printf.h"
26
27 /* Read a simple integer from a string and update the string pointer.
28    It is assumed that the first character is a digit.  */
29 static unsigned int
30 read_int (const char **pstr)
31 {
32   unsigned int retval = (unsigned char) **pstr - '0';
33
34   while (isdigit ((unsigned char) *++(*pstr)))
35     {
36       retval *= 10;
37       retval += (unsigned char) **pstr - '0';
38     }
39
40   return retval;
41 }
42
43 #define PADSIZE 16
44 static char const blanks[PADSIZE] =
45 {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
46 static char const zeroes[PADSIZE] =
47 {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
48 static wchar_t const wblanks[PADSIZE] =
49 {
50   L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '),
51   L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' ')
52 };
53 static wchar_t const wzeroes[PADSIZE] =
54 {
55   L_('0'), L_('0'), L_('0'), L_('0'), L_('0'), L_('0'), L_('0'), L_('0'),
56   L_('0'), L_('0'), L_('0'), L_('0'), L_('0'), L_('0'), L_('0'), L_('0')
57 };
58
59 attribute_hidden size_t
60 __quadmath_do_pad (struct __quadmath_printf_file *fp, int wide, int c,
61                    size_t n)
62 {
63   ssize_t i;
64   char padbuf[PADSIZE];
65   wchar_t wpadbuf[PADSIZE];
66   const char *padstr;
67   size_t w, written = 0;
68   if (wide)
69     {
70       if (c == ' ')
71         padstr = (const char *) wblanks;
72       else if (c == '0')
73         padstr = (const char *) wzeroes;
74       else
75         {
76           padstr = (const char *) wpadbuf;
77           for (i = 0; i < PADSIZE; i++)
78             wpadbuf[i] = c;
79         }
80     }
81   else
82     {
83       if (c == ' ')
84         padstr = blanks;
85       else if (c == '0')
86         padstr = zeroes;
87       else
88         {
89           padstr = (const char *) padbuf;
90           for (i = 0; i < PADSIZE; i++)
91             padbuf[i] = c;
92         }
93     }
94   for (i = n; i >= PADSIZE; i -= PADSIZE)
95     {
96       w = PUT (fp, (char *) padstr, PADSIZE);
97       written += w;
98       if (w != PADSIZE)
99         return written;
100     }
101   if (i > 0)
102     {
103       w = PUT (fp, (char *) padstr, i);
104       written += w;
105     }
106   return written;
107 }
108
109 /* This is a stripped down version of snprintf, which just handles
110    a single %eEfFgGaA format entry with Q modifier.  % has to be
111    the first character of the format string, no $ can be used.  */
112 int
113 quadmath_snprintf (char *str, size_t size, const char *format, ...)
114 {
115   struct printf_info info;
116   va_list ap;
117   __float128 fpnum, *fpnum_addr = &fpnum, **fpnum_addr2 = &fpnum_addr;
118   struct __quadmath_printf_file qfp;
119
120   if (*format++ != '%')
121     return -1;
122
123   /* Clear information structure.  */
124   memset (&info, '\0', sizeof info);
125   /* info.alt = 0;
126   info.space = 0;
127   info.left = 0;
128   info.showsign = 0;
129   info.group = 0;
130   info.i18n = 0;
131   info.extra = 0; */
132   info.pad = ' ';
133   /* info.wide = 0; */
134
135   /* Check for spec modifiers.  */
136   do
137     {
138       switch (*format)
139         {
140         case ' ':
141           /* Output a space in place of a sign, when there is no sign.  */
142           info.space = 1;
143           continue;
144         case '+':
145           /* Always output + or - for numbers.  */
146           info.showsign = 1;
147           continue;
148         case '-':
149           /* Left-justify things.  */
150           info.left = 1;
151           continue;
152         case '#':
153           /* Use the "alternate form":
154              Hex has 0x or 0X, FP always has a decimal point.  */
155           info.alt = 1;
156           continue;
157         case '0':
158           /* Pad with 0s.  */
159           info.pad = '0';
160           continue;
161         case '\'':
162           /* Show grouping in numbers if the locale information
163              indicates any.  */
164           info.group = 1;
165           continue;
166         case 'I':
167           /* Use the internationalized form of the output.  Currently
168              means to use the `outdigits' of the current locale.  */
169           info.i18n = 1;
170           continue;
171         default:
172           break;
173         }
174       break;
175     }
176   while (*++format);
177
178   if (info.left)
179     info.pad = ' ';
180
181   va_start (ap, format);
182
183   /* Get the field width.  */
184   /* info.width = 0; */
185   if (*format == '*')
186     {
187       /* The field width is given in an argument.
188          A negative field width indicates left justification.  */
189       ++format;
190       info.width = va_arg (ap, int);
191     }
192   else if (isdigit (*format))
193     /* Constant width specification.  */
194     info.width = read_int (&format);
195
196   /* Get the precision.  */
197   /* -1 means none given; 0 means explicit 0.  */
198   info.prec = -1;
199   if (*format == '.')
200     {
201       ++format;
202       if (*format == '*')
203         {
204           /* The precision is given in an argument.  */
205           ++format;
206
207           info.prec = va_arg (ap, int);
208         }
209       else if (isdigit (*format))
210         info.prec = read_int (&format);
211       else
212         /* "%.?" is treated like "%.0?".  */
213         info.prec = 0;
214     }
215
216   /* Check for type modifiers.  */
217   /* info.is_long_double = 0;
218   info.is_short = 0;
219   info.is_long = 0;
220   info.is_char = 0;
221   info.user = 0; */
222
223   /* We require Q modifier.  */
224   if (*format++ != 'Q')
225     {
226       va_end (ap);
227       return -1;
228     }
229
230   /* Get the format specification.  */
231   info.spec = (wchar_t) *format++;
232   if (info.spec == L_('\0') || *format != '\0')
233     {
234       va_end (ap);
235       return -1;
236     }
237
238   switch (info.spec)
239     {
240     case L_('e'):
241     case L_('E'):
242     case L_('f'):
243     case L_('F'):
244     case L_('g'):
245     case L_('G'):
246     case L_('a'):
247     case L_('A'):
248       break;
249     default:
250       va_end (ap);
251       return -1;
252     }
253
254   fpnum = va_arg (ap, __float128);
255   va_end (ap);
256
257   qfp.fp = NULL;
258   qfp.str = str;
259   qfp.size = size ? size - 1 : 0;
260   qfp.len = 0;
261   qfp.file_p = 0;
262
263   if (info.spec == L_('a') || info.spec == L_('A'))
264     __quadmath_printf_fphex (&qfp, &info, (const void *const *)&fpnum_addr2);
265   else
266     __quadmath_printf_fp (&qfp, &info, (const void *const *)&fpnum_addr2);
267
268   if (size)
269     *qfp.str = '\0';
270
271   return qfp.len;
272 }
273
274 #ifdef HAVE_PRINTF_HOOKS
275 static int pa_flt128;
276 int mod_Q attribute_hidden;
277
278 static void
279 flt128_va (void *mem, va_list *ap)
280
281   __float128 d = va_arg (*ap, __float128);
282   memcpy (mem, &d, sizeof (d));
283 }
284
285 static int
286 flt128_ais (const struct printf_info *info, size_t n __attribute__ ((unused)),
287             int *argtype, int *size)
288 {
289   if (info->user & mod_Q)
290     {
291       argtype[0] = pa_flt128;
292       size[0] = sizeof (__float128);
293       return 1;
294     }
295 #if __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 13)
296   /* Workaround bug in glibc printf hook handling.  */
297   size[0] = -1;
298   switch (info->spec)
299     {
300     case L_('i'):
301     case L_('d'):
302     case L_('u'):
303     case L_('o'):
304     case L_('X'):
305     case L_('x'):
306 #if __LONG_MAX__ != __LONG_LONG_MAX__
307       if (info->is_long_double)
308         argtype[0] = PA_INT|PA_FLAG_LONG_LONG;
309       else
310 #endif
311       if (info->is_long)
312         argtype[0] = PA_INT|PA_FLAG_LONG;
313       else if (info->is_short)
314         argtype[0] = PA_INT|PA_FLAG_SHORT;
315       else if (info->is_char)
316         argtype[0] = PA_CHAR;
317       else
318         argtype[0] = PA_INT;
319       return 1;
320     case L_('e'):
321     case L_('E'):
322     case L_('f'):
323     case L_('F'):
324     case L_('g'):
325     case L_('G'):
326     case L_('a'):
327     case L_('A'):
328       if (info->is_long_double)
329         argtype[0] = PA_DOUBLE|PA_FLAG_LONG_DOUBLE;
330       else
331         argtype[0] = PA_DOUBLE;
332       return 1;
333     case L_('c'):
334       argtype[0] = PA_CHAR;
335       return 1;
336     case L_('C'):
337       argtype[0] = PA_WCHAR;
338       return 1;
339     case L_('s'):
340       argtype[0] = PA_STRING;
341       return 1;
342     case L_('S'):
343       argtype[0] = PA_WSTRING;
344       return 1;
345     case L_('p'):
346       argtype[0] = PA_POINTER;
347       return 1;
348     case L_('n'):
349       argtype[0] = PA_INT|PA_FLAG_PTR;
350       return 1;
351
352     case L_('m'):
353     default:
354       /* An unknown spec will consume no args.  */
355       return 0;
356     }
357 #endif
358   return -1;
359 }
360
361 static int
362 flt128_printf_fp (FILE *fp, const struct printf_info *info,
363                   const void *const *args)
364 {
365   struct __quadmath_printf_file qpf
366     = { .fp = fp, .str = NULL, .size = 0, .len = 0, .file_p = 1 };
367
368   if ((info->user & mod_Q) == 0)
369     return -2;
370
371   return __quadmath_printf_fp (&qpf, info, args);
372 }
373
374 static int
375 flt128_printf_fphex (FILE *fp, const struct printf_info *info,
376                      const void *const *args)
377 {
378   struct __quadmath_printf_file qpf
379     = { .fp = fp, .str = NULL, .size = 0, .len = 0, .file_p = 1 };
380
381   if ((info->user & mod_Q) == 0)
382     return -2;
383
384   return __quadmath_printf_fphex (&qpf, info, args);
385 }
386
387 __attribute__((constructor)) static void
388 register_printf_flt128 (void)
389 {
390   pa_flt128 = register_printf_type (flt128_va);
391   if (pa_flt128 == -1)
392     return;
393   mod_Q = register_printf_modifier (L_("Q"));
394   if (mod_Q == -1)
395     return;
396   register_printf_specifier ('f', flt128_printf_fp, flt128_ais);
397   register_printf_specifier ('F', flt128_printf_fp, flt128_ais);
398   register_printf_specifier ('e', flt128_printf_fp, flt128_ais);
399   register_printf_specifier ('E', flt128_printf_fp, flt128_ais);
400   register_printf_specifier ('g', flt128_printf_fp, flt128_ais);
401   register_printf_specifier ('G', flt128_printf_fp, flt128_ais);
402   register_printf_specifier ('a', flt128_printf_fphex, flt128_ais);
403   register_printf_specifier ('A', flt128_printf_fphex, flt128_ais);
404 }
405
406 __attribute__((destructor)) static void
407 unregister_printf_flt128 (void)
408 {
409   /* No way to unregister printf type and modifier currently,
410      and only one printf specifier can be registered right now.  */
411   if (pa_flt128 == -1 || mod_Q == -1)
412     return;
413   register_printf_specifier ('f', NULL, NULL);
414   register_printf_specifier ('F', NULL, NULL);
415   register_printf_specifier ('e', NULL, NULL);
416   register_printf_specifier ('E', NULL, NULL);
417   register_printf_specifier ('g', NULL, NULL);
418   register_printf_specifier ('G', NULL, NULL);
419   register_printf_specifier ('a', NULL, NULL);
420   register_printf_specifier ('A', NULL, NULL);
421 }
422 #endif