1/* GCC Quad-Precision Math Library
2   Copyright (C) 2011 Free Software Foundation, Inc.
3   Written by Jakub Jelinek  <jakub@redhat.com>
4
5This file is part of the libquadmath library.
6Libquadmath is free software; you can redistribute it and/or
7modify it under the terms of the GNU Library General Public
8License as published by the Free Software Foundation; either
9version 2 of the License, or (at your option) any later version.
10
11Libquadmath is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14Library General Public License for more details.
15
16You should have received a copy of the GNU Library General Public
17License along with libquadmath; see the file COPYING.LIB.  If
18not, write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
19Boston, MA 02110-1301, USA.  */
20
21#include <config.h>
22#include <stdarg.h>
23#include <string.h>
24#include <stdio.h>
25#include "quadmath-printf.h"
26
27/* Read a simple integer from a string and update the string pointer.
28   It is assumed that the first character is a digit.  */
29static unsigned int
30read_int (const char **pstr)
31{
32  unsigned int retval = (unsigned char) **pstr - '0';
33
34  while (isdigit ((unsigned char) *++(*pstr)))
35    {
36      retval *= 10;
37      retval += (unsigned char) **pstr - '0';
38    }
39
40  return retval;
41}
42
43#define PADSIZE 16
44static char const blanks[PADSIZE] =
45{' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
46static char const zeroes[PADSIZE] =
47{'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
48static wchar_t const wblanks[PADSIZE] =
49{
50  L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '),
51  L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' ')
52};
53static wchar_t const wzeroes[PADSIZE] =
54{
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')
57};
58
59attribute_hidden size_t
60__quadmath_do_pad (struct __quadmath_printf_file *fp, int wide, int c,
61		   size_t n)
62{
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)
69    {
70      if (c == ' ')
71	padstr = (const char *) wblanks;
72      else if (c == '0')
73	padstr = (const char *) wzeroes;
74      else
75	{
76	  padstr = (const char *) wpadbuf;
77	  for (i = 0; i < PADSIZE; i++)
78	    wpadbuf[i] = c;
79	}
80    }
81  else
82    {
83      if (c == ' ')
84	padstr = blanks;
85      else if (c == '0')
86	padstr = zeroes;
87      else
88	{
89	  padstr = (const char *) padbuf;
90	  for (i = 0; i < PADSIZE; i++)
91	    padbuf[i] = c;
92	}
93    }
94  for (i = n; i >= PADSIZE; i -= PADSIZE)
95    {
96      w = PUT (fp, (char *) padstr, PADSIZE);
97      written += w;
98      if (w != PADSIZE)
99	return written;
100    }
101  if (i > 0)
102    {
103      w = PUT (fp, (char *) padstr, i);
104      written += w;
105    }
106  return written;
107}
108
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.  */
112int
113quadmath_snprintf (char *str, size_t size, const char *format, ...)
114{
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;
119
120  if (*format++ != '%')
121    return -1;
122
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; */
134
135  /* Check for spec modifiers.  */
136  do
137    {
138      switch (*format)
139	{
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;
173	}
174      break;
175    }
176  while (*++format);
177
178  if (info.left)
179    info.pad = ' ';
180
181  va_start (ap, format);
182
183  /* Get the field width.  */
184  /* info.width = 0; */
185  if (*format == '*')
186    {
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);
191    }
192  else if (isdigit (*format))
193    /* Constant width specification.  */
194    info.width = read_int (&format);
195
196  /* Get the precision.  */
197  /* -1 means none given; 0 means explicit 0.  */
198  info.prec = -1;
199  if (*format == '.')
200    {
201      ++format;
202      if (*format == '*')
203	{
204	  /* The precision is given in an argument.  */
205	  ++format;
206
207	  info.prec = va_arg (ap, int);
208	}
209      else if (isdigit (*format))
210	info.prec = read_int (&format);
211      else
212	/* "%.?" is treated like "%.0?".  */
213	info.prec = 0;
214    }
215
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; */
222
223  /* We require Q modifier.  */
224  if (*format++ != 'Q')
225    {
226      va_end (ap);
227      return -1;
228    }
229
230  /* Get the format specification.  */
231  info.spec = (wchar_t) *format++;
232  if (info.spec == L_('\0') || *format != '\0')
233    {
234      va_end (ap);
235      return -1;
236    }
237
238  switch (info.spec)
239    {
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;
252    }
253
254  fpnum = va_arg (ap, __float128);
255  va_end (ap);
256
257  qfp.fp = NULL;
258  qfp.str = str;
259  qfp.size = size ? size - 1 : 0;
260  qfp.len = 0;
261  qfp.file_p = 0;
262
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);
267
268  if (size)
269    *qfp.str = '\0';
270
271  return qfp.len;
272}
273
274#ifdef HAVE_PRINTF_HOOKS
275static int pa_flt128;
276int mod_Q attribute_hidden;
277
278static void
279flt128_va (void *mem, va_list *ap)
280{
281  __float128 d = va_arg (*ap, __float128);
282  memcpy (mem, &d, sizeof (d));
283}
284
285static int
286flt128_ais (const struct printf_info *info, size_t n __attribute__ ((unused)),
287	    int *argtype, int *size)
288{
289  if (info->user & mod_Q)
290    {
291      argtype[0] = pa_flt128;
292      size[0] = sizeof (__float128);
293      return 1;
294    }
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)
299    {
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;
351
352    case L_('m'):
353    default:
354      /* An unknown spec will consume no args.  */
355      return 0;
356    }
357#endif
358  return -1;
359}
360
361static int
362flt128_printf_fp (FILE *fp, const struct printf_info *info,
363		  const void *const *args)
364{
365  struct __quadmath_printf_file qpf
366    = { .fp = fp, .str = NULL, .size = 0, .len = 0, .file_p = 1 };
367
368  if ((info->user & mod_Q) == 0)
369    return -2;
370
371  return __quadmath_printf_fp (&qpf, info, args);
372}
373
374static int
375flt128_printf_fphex (FILE *fp, const struct printf_info *info,
376		     const void *const *args)
377{
378  struct __quadmath_printf_file qpf
379    = { .fp = fp, .str = NULL, .size = 0, .len = 0, .file_p = 1 };
380
381  if ((info->user & mod_Q) == 0)
382    return -2;
383
384  return __quadmath_printf_fphex (&qpf, info, args);
385}
386
387__attribute__((constructor)) static void
388register_printf_flt128 (void)
389{
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);
404}
405
406__attribute__((destructor)) static void
407unregister_printf_flt128 (void)
408{
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);
421}
422#endif
423