]>
Commit | Line | Data |
---|---|---|
1d92226b JJ |
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. */ | |
12967872 JJ |
124 | memset (&info, '\0', sizeof info); |
125 | /* info.alt = 0; | |
1d92226b JJ |
126 | info.space = 0; |
127 | info.left = 0; | |
128 | info.showsign = 0; | |
129 | info.group = 0; | |
130 | info.i18n = 0; | |
12967872 | 131 | info.extra = 0; */ |
1d92226b | 132 | info.pad = ' '; |
12967872 | 133 | /* info.wide = 0; */ |
1d92226b JJ |
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. */ | |
12967872 | 184 | /* info.width = 0; */ |
1d92226b JJ |
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. */ | |
12967872 | 217 | /* info.is_long_double = 0; |
1d92226b JJ |
218 | info.is_short = 0; |
219 | info.is_long = 0; | |
220 | info.is_char = 0; | |
12967872 | 221 | info.user = 0; */ |
1d92226b JJ |
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; | |
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 (qfp.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_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 |