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