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