1 | /* GCC Quad-Precision Math Library |
2 | Copyright (C) 2011 Free Software Foundation, Inc. |
3 | Written by Jakub Jelinek <jakub@redhat.com> |
4 | |
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. |
10 | |
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. |
15 | |
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. */ |
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. */ |
29 | static unsigned int |
30 | read_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 |
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] = |
49 | { |
50 | L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), |
51 | L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' '), L_(' ') |
52 | }; |
53 | static 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 | |
59 | attribute_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. */ |
112 | int |
113 | quadmath_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 |
275 | static int pa_flt128; |
276 | int mod_Q attribute_hidden; |
277 | |
278 | static void |
279 | flt128_va (void *mem, va_list *ap) |
280 | { |
281 | __float128 d = va_arg (*ap, __float128); |
282 | memcpy (mem, &d, sizeof (d)); |
283 | } |
284 | |
285 | static int |
286 | flt128_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 | |
361 | static int |
362 | flt128_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 | |
374 | static int |
375 | flt128_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 |
388 | register_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 |
407 | unregister_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 | |