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