1 /* GCC Quad-Precision Math Library
2 Copyright (C) 2011 Free Software Foundation, Inc.
3 Written by Jakub Jelinek <jakub@redhat.com>
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.
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.
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. */
25 #include "quadmath-printf.h"
27 /* Read a simple integer from a string and update the string pointer.
28 It is assumed that the first character is a digit. */
30 read_int (const char **pstr)
32 unsigned int retval = (unsigned char) **pstr - '0';
34 while (isdigit ((unsigned char) *++(*pstr)))
37 retval += (unsigned char) **pstr - '0';
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] =
50 L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '),
51 L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' ')
53 static wchar_t const wzeroes[PADSIZE] =
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')
59 attribute_hidden size_t
60 __quadmath_do_pad (struct __quadmath_printf_file *fp, int wide, int c,
65 wchar_t wpadbuf[PADSIZE];
67 size_t w, written = 0;
71 padstr = (const char *) wblanks;
73 padstr = (const char *) wzeroes;
76 padstr = (const char *) wpadbuf;
77 for (i = 0; i < PADSIZE; i++)
89 padstr = (const char *) padbuf;
90 for (i = 0; i < PADSIZE; i++)
94 for (i = n; i >= PADSIZE; i -= PADSIZE)
96 w = PUT (fp, (char *) padstr, PADSIZE);
103 w = PUT (fp, (char *) padstr, i);
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. */
113 quadmath_snprintf (char *str, size_t size, const char *format, ...)
115 struct printf_info info;
117 __float128 fpnum, *fpnum_addr = &fpnum, **fpnum_addr2 = &fpnum_addr;
118 struct __quadmath_printf_file qfp;
120 if (*format++ != '%')
123 /* Clear information structure. */
124 memset (&info, '\0', sizeof info);
135 /* Check for spec modifiers. */
141 /* Output a space in place of a sign, when there is no sign. */
145 /* Always output + or - for numbers. */
149 /* Left-justify things. */
153 /* Use the "alternate form":
154 Hex has 0x or 0X, FP always has a decimal point. */
162 /* Show grouping in numbers if the locale information
167 /* Use the internationalized form of the output. Currently
168 means to use the `outdigits' of the current locale. */
181 va_start (ap, format);
183 /* Get the field width. */
184 /* info.width = 0; */
187 /* The field width is given in an argument.
188 A negative field width indicates left justification. */
190 info.width = va_arg (ap, int);
192 else if (isdigit (*format))
193 /* Constant width specification. */
194 info.width = read_int (&format);
196 /* Get the precision. */
197 /* -1 means none given; 0 means explicit 0. */
204 /* The precision is given in an argument. */
207 info.prec = va_arg (ap, int);
209 else if (isdigit (*format))
210 info.prec = read_int (&format);
212 /* "%.?" is treated like "%.0?". */
216 /* Check for type modifiers. */
217 /* info.is_long_double = 0;
223 /* We require Q modifier. */
224 if (*format++ != 'Q')
230 /* Get the format specification. */
231 info.spec = (wchar_t) *format++;
232 if (info.spec == L_('\0') || *format != '\0')
254 fpnum = va_arg (ap, __float128);
259 qfp.size = size ? size - 1 : 0;
263 if (info.spec == L_('a') || info.spec == L_('A'))
264 __quadmath_printf_fphex (&qfp, &info, (const void *const *)&fpnum_addr2);
266 __quadmath_printf_fp (&qfp, &info, (const void *const *)&fpnum_addr2);
274 #ifdef HAVE_PRINTF_HOOKS
275 static int pa_flt128;
276 int mod_Q attribute_hidden;
279 flt128_va (void *mem, va_list *ap)
281 __float128 d = va_arg (*ap, __float128);
282 memcpy (mem, &d, sizeof (d));
286 flt128_ais (const struct printf_info *info, size_t n __attribute__ ((unused)),
287 int *argtype, int *size)
289 if (info->user & mod_Q)
291 argtype[0] = pa_flt128;
292 size[0] = sizeof (__float128);
295 #if __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 13)
296 /* Workaround bug in glibc printf hook handling. */
306 #if __LONG_MAX__ != __LONG_LONG_MAX__
307 if (info->is_long_double)
308 argtype[0] = PA_INT|PA_FLAG_LONG_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;
328 if (info->is_long_double)
329 argtype[0] = PA_DOUBLE|PA_FLAG_LONG_DOUBLE;
331 argtype[0] = PA_DOUBLE;
334 argtype[0] = PA_CHAR;
337 argtype[0] = PA_WCHAR;
340 argtype[0] = PA_STRING;
343 argtype[0] = PA_WSTRING;
346 argtype[0] = PA_POINTER;
349 argtype[0] = PA_INT|PA_FLAG_PTR;
354 /* An unknown spec will consume no args. */
362 flt128_printf_fp (FILE *fp, const struct printf_info *info,
363 const void *const *args)
365 struct __quadmath_printf_file qpf
366 = { .fp = fp, .str = NULL, .size = 0, .len = 0, .file_p = 1 };
368 if ((info->user & mod_Q) == 0)
371 return __quadmath_printf_fp (&qpf, info, args);
375 flt128_printf_fphex (FILE *fp, const struct printf_info *info,
376 const void *const *args)
378 struct __quadmath_printf_file qpf
379 = { .fp = fp, .str = NULL, .size = 0, .len = 0, .file_p = 1 };
381 if ((info->user & mod_Q) == 0)
384 return __quadmath_printf_fphex (&qpf, info, args);
387 __attribute__((constructor)) static void
388 register_printf_flt128 (void)
390 pa_flt128 = register_printf_type (flt128_va);
393 mod_Q = register_printf_modifier (L_("Q"));
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);
406 __attribute__((destructor)) static void
407 unregister_printf_flt128 (void)
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)
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);