aboutsummaryrefslogtreecommitdiffstats
path: root/gcc-4.9/libquadmath/printf/quadmath-printf.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc-4.9/libquadmath/printf/quadmath-printf.c')
-rw-r--r--gcc-4.9/libquadmath/printf/quadmath-printf.c422
1 files changed, 422 insertions, 0 deletions
diff --git a/gcc-4.9/libquadmath/printf/quadmath-printf.c b/gcc-4.9/libquadmath/printf/quadmath-printf.c
new file mode 100644
index 000000000..b70f432cc
--- /dev/null
+++ b/gcc-4.9/libquadmath/printf/quadmath-printf.c
@@ -0,0 +1,422 @@
+/* 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