PR middle-end/67452
[official-gcc.git] / libquadmath / printf / quadmath-printf.c
blobb70f432cce02ca836acf3f1c55b2e0d150ce04ac
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. */
21 #include <config.h>
22 #include <stdarg.h>
23 #include <string.h>
24 #include <stdio.h>
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. */
29 static unsigned int
30 read_int (const char **pstr)
32 unsigned int retval = (unsigned char) **pstr - '0';
34 while (isdigit ((unsigned char) *++(*pstr)))
36 retval *= 10;
37 retval += (unsigned char) **pstr - '0';
40 return retval;
43 #define PADSIZE 16
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,
61 size_t n)
63 ssize_t i;
64 char padbuf[PADSIZE];
65 wchar_t wpadbuf[PADSIZE];
66 const char *padstr;
67 size_t w, written = 0;
68 if (wide)
70 if (c == ' ')
71 padstr = (const char *) wblanks;
72 else if (c == '0')
73 padstr = (const char *) wzeroes;
74 else
76 padstr = (const char *) wpadbuf;
77 for (i = 0; i < PADSIZE; i++)
78 wpadbuf[i] = c;
81 else
83 if (c == ' ')
84 padstr = blanks;
85 else if (c == '0')
86 padstr = zeroes;
87 else
89 padstr = (const char *) padbuf;
90 for (i = 0; i < PADSIZE; i++)
91 padbuf[i] = c;
94 for (i = n; i >= PADSIZE; i -= PADSIZE)
96 w = PUT (fp, (char *) padstr, PADSIZE);
97 written += w;
98 if (w != PADSIZE)
99 return written;
101 if (i > 0)
103 w = PUT (fp, (char *) padstr, i);
104 written += w;
106 return written;
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;
116 va_list ap;
117 __float128 fpnum, *fpnum_addr = &fpnum, **fpnum_addr2 = &fpnum_addr;
118 struct __quadmath_printf_file qfp;
120 if (*format++ != '%')
121 return -1;
123 /* Clear information structure. */
124 memset (&info, '\0', sizeof info);
125 /* info.alt = 0;
126 info.space = 0;
127 info.left = 0;
128 info.showsign = 0;
129 info.group = 0;
130 info.i18n = 0;
131 info.extra = 0; */
132 info.pad = ' ';
133 /* info.wide = 0; */
135 /* Check for spec modifiers. */
138 switch (*format)
140 case ' ':
141 /* Output a space in place of a sign, when there is no sign. */
142 info.space = 1;
143 continue;
144 case '+':
145 /* Always output + or - for numbers. */
146 info.showsign = 1;
147 continue;
148 case '-':
149 /* Left-justify things. */
150 info.left = 1;
151 continue;
152 case '#':
153 /* Use the "alternate form":
154 Hex has 0x or 0X, FP always has a decimal point. */
155 info.alt = 1;
156 continue;
157 case '0':
158 /* Pad with 0s. */
159 info.pad = '0';
160 continue;
161 case '\'':
162 /* Show grouping in numbers if the locale information
163 indicates any. */
164 info.group = 1;
165 continue;
166 case 'I':
167 /* Use the internationalized form of the output. Currently
168 means to use the `outdigits' of the current locale. */
169 info.i18n = 1;
170 continue;
171 default:
172 break;
174 break;
176 while (*++format);
178 if (info.left)
179 info.pad = ' ';
181 va_start (ap, format);
183 /* Get the field width. */
184 /* info.width = 0; */
185 if (*format == '*')
187 /* The field width is given in an argument.
188 A negative field width indicates left justification. */
189 ++format;
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. */
198 info.prec = -1;
199 if (*format == '.')
201 ++format;
202 if (*format == '*')
204 /* The precision is given in an argument. */
205 ++format;
207 info.prec = va_arg (ap, int);
209 else if (isdigit (*format))
210 info.prec = read_int (&format);
211 else
212 /* "%.?" is treated like "%.0?". */
213 info.prec = 0;
216 /* Check for type modifiers. */
217 /* info.is_long_double = 0;
218 info.is_short = 0;
219 info.is_long = 0;
220 info.is_char = 0;
221 info.user = 0; */
223 /* We require Q modifier. */
224 if (*format++ != 'Q')
226 va_end (ap);
227 return -1;
230 /* Get the format specification. */
231 info.spec = (wchar_t) *format++;
232 if (info.spec == L_('\0') || *format != '\0')
234 va_end (ap);
235 return -1;
238 switch (info.spec)
240 case L_('e'):
241 case L_('E'):
242 case L_('f'):
243 case L_('F'):
244 case L_('g'):
245 case L_('G'):
246 case L_('a'):
247 case L_('A'):
248 break;
249 default:
250 va_end (ap);
251 return -1;
254 fpnum = va_arg (ap, __float128);
255 va_end (ap);
257 qfp.fp = NULL;
258 qfp.str = str;
259 qfp.size = size ? size - 1 : 0;
260 qfp.len = 0;
261 qfp.file_p = 0;
263 if (info.spec == L_('a') || info.spec == L_('A'))
264 __quadmath_printf_fphex (&qfp, &info, (const void *const *)&fpnum_addr2);
265 else
266 __quadmath_printf_fp (&qfp, &info, (const void *const *)&fpnum_addr2);
268 if (size)
269 *qfp.str = '\0';
271 return qfp.len;
274 #ifdef HAVE_PRINTF_HOOKS
275 static int pa_flt128;
276 int mod_Q attribute_hidden;
278 static void
279 flt128_va (void *mem, va_list *ap)
281 __float128 d = va_arg (*ap, __float128);
282 memcpy (mem, &d, sizeof (d));
285 static int
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);
293 return 1;
295 #if __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 13)
296 /* Workaround bug in glibc printf hook handling. */
297 size[0] = -1;
298 switch (info->spec)
300 case L_('i'):
301 case L_('d'):
302 case L_('u'):
303 case L_('o'):
304 case L_('X'):
305 case L_('x'):
306 #if __LONG_MAX__ != __LONG_LONG_MAX__
307 if (info->is_long_double)
308 argtype[0] = PA_INT|PA_FLAG_LONG_LONG;
309 else
310 #endif
311 if (info->is_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;
317 else
318 argtype[0] = PA_INT;
319 return 1;
320 case L_('e'):
321 case L_('E'):
322 case L_('f'):
323 case L_('F'):
324 case L_('g'):
325 case L_('G'):
326 case L_('a'):
327 case L_('A'):
328 if (info->is_long_double)
329 argtype[0] = PA_DOUBLE|PA_FLAG_LONG_DOUBLE;
330 else
331 argtype[0] = PA_DOUBLE;
332 return 1;
333 case L_('c'):
334 argtype[0] = PA_CHAR;
335 return 1;
336 case L_('C'):
337 argtype[0] = PA_WCHAR;
338 return 1;
339 case L_('s'):
340 argtype[0] = PA_STRING;
341 return 1;
342 case L_('S'):
343 argtype[0] = PA_WSTRING;
344 return 1;
345 case L_('p'):
346 argtype[0] = PA_POINTER;
347 return 1;
348 case L_('n'):
349 argtype[0] = PA_INT|PA_FLAG_PTR;
350 return 1;
352 case L_('m'):
353 default:
354 /* An unknown spec will consume no args. */
355 return 0;
357 #endif
358 return -1;
361 static int
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)
369 return -2;
371 return __quadmath_printf_fp (&qpf, info, args);
374 static int
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)
382 return -2;
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);
391 if (pa_flt128 == -1)
392 return;
393 mod_Q = register_printf_modifier (L_("Q"));
394 if (mod_Q == -1)
395 return;
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)
412 return;
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);
422 #endif