123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423 |
- /* GCC Quad-Precision Math Library
- Copyright (C) 2011 Free Software Foundation, Inc.
- Written by Jakub Jelinek <jakub@redhat.com>
- This file is part of the libquadmath library.
- Libquadmath is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public
- License as published by the Free Software Foundation; either
- version 2 of the License, or (at your option) any later version.
- Libquadmath is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Library General Public License for more details.
- You should have received a copy of the GNU Library General Public
- License along with libquadmath; see the file COPYING.LIB. If
- not, write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
- Boston, MA 02110-1301, USA. */
- #include <config.h>
- #include <stdarg.h>
- #include <string.h>
- #include <stdio.h>
- #include "quadmath-printf.h"
- /* Read a simple integer from a string and update the string pointer.
- It is assumed that the first character is a digit. */
- static unsigned int
- read_int (const char **pstr)
- {
- unsigned int retval = (unsigned char) **pstr - '0';
- while (isdigit ((unsigned char) *++(*pstr)))
- {
- retval *= 10;
- retval += (unsigned char) **pstr - '0';
- }
- return retval;
- }
- #define PADSIZE 16
- static char const blanks[PADSIZE] =
- {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
- static char const zeroes[PADSIZE] =
- {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
- static wchar_t const wblanks[PADSIZE] =
- {
- L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '),
- L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' ')
- };
- static wchar_t const wzeroes[PADSIZE] =
- {
- L_('0'), L_('0'), L_('0'), L_('0'), L_('0'), L_('0'), L_('0'), L_('0'),
- L_('0'), L_('0'), L_('0'), L_('0'), L_('0'), L_('0'), L_('0'), L_('0')
- };
- attribute_hidden size_t
- __quadmath_do_pad (struct __quadmath_printf_file *fp, int wide, int c,
- size_t n)
- {
- ssize_t i;
- char padbuf[PADSIZE];
- wchar_t wpadbuf[PADSIZE];
- const char *padstr;
- size_t w, written = 0;
- if (wide)
- {
- if (c == ' ')
- padstr = (const char *) wblanks;
- else if (c == '0')
- padstr = (const char *) wzeroes;
- else
- {
- padstr = (const char *) wpadbuf;
- for (i = 0; i < PADSIZE; i++)
- wpadbuf[i] = c;
- }
- }
- else
- {
- if (c == ' ')
- padstr = blanks;
- else if (c == '0')
- padstr = zeroes;
- else
- {
- padstr = (const char *) padbuf;
- for (i = 0; i < PADSIZE; i++)
- padbuf[i] = c;
- }
- }
- for (i = n; i >= PADSIZE; i -= PADSIZE)
- {
- w = PUT (fp, (char *) padstr, PADSIZE);
- written += w;
- if (w != PADSIZE)
- return written;
- }
- if (i > 0)
- {
- w = PUT (fp, (char *) padstr, i);
- written += w;
- }
- return written;
- }
- /* This is a stripped down version of snprintf, which just handles
- a single %eEfFgGaA format entry with Q modifier. % has to be
- the first character of the format string, no $ can be used. */
- int
- quadmath_snprintf (char *str, size_t size, const char *format, ...)
- {
- struct printf_info info;
- va_list ap;
- __float128 fpnum, *fpnum_addr = &fpnum, **fpnum_addr2 = &fpnum_addr;
- struct __quadmath_printf_file qfp;
- if (*format++ != '%')
- return -1;
- /* Clear information structure. */
- memset (&info, '\0', sizeof info);
- /* info.alt = 0;
- info.space = 0;
- info.left = 0;
- info.showsign = 0;
- info.group = 0;
- info.i18n = 0;
- info.extra = 0; */
- info.pad = ' ';
- /* info.wide = 0; */
- /* Check for spec modifiers. */
- do
- {
- switch (*format)
- {
- case ' ':
- /* Output a space in place of a sign, when there is no sign. */
- info.space = 1;
- continue;
- case '+':
- /* Always output + or - for numbers. */
- info.showsign = 1;
- continue;
- case '-':
- /* Left-justify things. */
- info.left = 1;
- continue;
- case '#':
- /* Use the "alternate form":
- Hex has 0x or 0X, FP always has a decimal point. */
- info.alt = 1;
- continue;
- case '0':
- /* Pad with 0s. */
- info.pad = '0';
- continue;
- case '\'':
- /* Show grouping in numbers if the locale information
- indicates any. */
- info.group = 1;
- continue;
- case 'I':
- /* Use the internationalized form of the output. Currently
- means to use the `outdigits' of the current locale. */
- info.i18n = 1;
- continue;
- default:
- break;
- }
- break;
- }
- while (*++format);
- if (info.left)
- info.pad = ' ';
- va_start (ap, format);
- /* Get the field width. */
- /* info.width = 0; */
- if (*format == '*')
- {
- /* The field width is given in an argument.
- A negative field width indicates left justification. */
- ++format;
- info.width = va_arg (ap, int);
- }
- else if (isdigit (*format))
- /* Constant width specification. */
- info.width = read_int (&format);
- /* Get the precision. */
- /* -1 means none given; 0 means explicit 0. */
- info.prec = -1;
- if (*format == '.')
- {
- ++format;
- if (*format == '*')
- {
- /* The precision is given in an argument. */
- ++format;
- info.prec = va_arg (ap, int);
- }
- else if (isdigit (*format))
- info.prec = read_int (&format);
- else
- /* "%.?" is treated like "%.0?". */
- info.prec = 0;
- }
- /* Check for type modifiers. */
- /* info.is_long_double = 0;
- info.is_short = 0;
- info.is_long = 0;
- info.is_char = 0;
- info.user = 0; */
- /* We require Q modifier. */
- if (*format++ != 'Q')
- {
- va_end (ap);
- return -1;
- }
- /* Get the format specification. */
- info.spec = (wchar_t) *format++;
- if (info.spec == L_('\0') || *format != '\0')
- {
- va_end (ap);
- return -1;
- }
- switch (info.spec)
- {
- case L_('e'):
- case L_('E'):
- case L_('f'):
- case L_('F'):
- case L_('g'):
- case L_('G'):
- case L_('a'):
- case L_('A'):
- break;
- default:
- va_end (ap);
- return -1;
- }
- fpnum = va_arg (ap, __float128);
- va_end (ap);
- qfp.fp = NULL;
- qfp.str = str;
- qfp.size = size ? size - 1 : 0;
- qfp.len = 0;
- qfp.file_p = 0;
- if (info.spec == L_('a') || info.spec == L_('A'))
- __quadmath_printf_fphex (&qfp, &info, (const void *const *)&fpnum_addr2);
- else
- __quadmath_printf_fp (&qfp, &info, (const void *const *)&fpnum_addr2);
- if (size)
- *qfp.str = '\0';
- return qfp.len;
- }
- #ifdef HAVE_PRINTF_HOOKS
- static int pa_flt128;
- int mod_Q attribute_hidden;
- static void
- flt128_va (void *mem, va_list *ap)
- {
- __float128 d = va_arg (*ap, __float128);
- memcpy (mem, &d, sizeof (d));
- }
- static int
- flt128_ais (const struct printf_info *info, size_t n __attribute__ ((unused)),
- int *argtype, int *size)
- {
- if (info->user & mod_Q)
- {
- argtype[0] = pa_flt128;
- size[0] = sizeof (__float128);
- return 1;
- }
- #if __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 13)
- /* Workaround bug in glibc printf hook handling. */
- size[0] = -1;
- switch (info->spec)
- {
- case L_('i'):
- case L_('d'):
- case L_('u'):
- case L_('o'):
- case L_('X'):
- case L_('x'):
- #if __LONG_MAX__ != __LONG_LONG_MAX__
- if (info->is_long_double)
- argtype[0] = PA_INT|PA_FLAG_LONG_LONG;
- else
- #endif
- if (info->is_long)
- argtype[0] = PA_INT|PA_FLAG_LONG;
- else if (info->is_short)
- argtype[0] = PA_INT|PA_FLAG_SHORT;
- else if (info->is_char)
- argtype[0] = PA_CHAR;
- else
- argtype[0] = PA_INT;
- return 1;
- case L_('e'):
- case L_('E'):
- case L_('f'):
- case L_('F'):
- case L_('g'):
- case L_('G'):
- case L_('a'):
- case L_('A'):
- if (info->is_long_double)
- argtype[0] = PA_DOUBLE|PA_FLAG_LONG_DOUBLE;
- else
- argtype[0] = PA_DOUBLE;
- return 1;
- case L_('c'):
- argtype[0] = PA_CHAR;
- return 1;
- case L_('C'):
- argtype[0] = PA_WCHAR;
- return 1;
- case L_('s'):
- argtype[0] = PA_STRING;
- return 1;
- case L_('S'):
- argtype[0] = PA_WSTRING;
- return 1;
- case L_('p'):
- argtype[0] = PA_POINTER;
- return 1;
- case L_('n'):
- argtype[0] = PA_INT|PA_FLAG_PTR;
- return 1;
- case L_('m'):
- default:
- /* An unknown spec will consume no args. */
- return 0;
- }
- #endif
- return -1;
- }
- static int
- flt128_printf_fp (FILE *fp, const struct printf_info *info,
- const void *const *args)
- {
- struct __quadmath_printf_file qpf
- = { .fp = fp, .str = NULL, .size = 0, .len = 0, .file_p = 1 };
- if ((info->user & mod_Q) == 0)
- return -2;
- return __quadmath_printf_fp (&qpf, info, args);
- }
- static int
- flt128_printf_fphex (FILE *fp, const struct printf_info *info,
- const void *const *args)
- {
- struct __quadmath_printf_file qpf
- = { .fp = fp, .str = NULL, .size = 0, .len = 0, .file_p = 1 };
- if ((info->user & mod_Q) == 0)
- return -2;
- return __quadmath_printf_fphex (&qpf, info, args);
- }
- __attribute__((constructor)) static void
- register_printf_flt128 (void)
- {
- pa_flt128 = register_printf_type (flt128_va);
- if (pa_flt128 == -1)
- return;
- mod_Q = register_printf_modifier (L_("Q"));
- if (mod_Q == -1)
- return;
- register_printf_specifier ('f', flt128_printf_fp, flt128_ais);
- register_printf_specifier ('F', flt128_printf_fp, flt128_ais);
- register_printf_specifier ('e', flt128_printf_fp, flt128_ais);
- register_printf_specifier ('E', flt128_printf_fp, flt128_ais);
- register_printf_specifier ('g', flt128_printf_fp, flt128_ais);
- register_printf_specifier ('G', flt128_printf_fp, flt128_ais);
- register_printf_specifier ('a', flt128_printf_fphex, flt128_ais);
- register_printf_specifier ('A', flt128_printf_fphex, flt128_ais);
- }
- __attribute__((destructor)) static void
- unregister_printf_flt128 (void)
- {
- /* No way to unregister printf type and modifier currently,
- and only one printf specifier can be registered right now. */
- if (pa_flt128 == -1 || mod_Q == -1)
- return;
- register_printf_specifier ('f', NULL, NULL);
- register_printf_specifier ('F', NULL, NULL);
- register_printf_specifier ('e', NULL, NULL);
- register_printf_specifier ('E', NULL, NULL);
- register_printf_specifier ('g', NULL, NULL);
- register_printf_specifier ('G', NULL, NULL);
- register_printf_specifier ('a', NULL, NULL);
- register_printf_specifier ('A', NULL, NULL);
- }
- #endif
|