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