1/* __gmp_doprnt -- printf style formatted output. 2 3 THE FUNCTIONS IN THIS FILE ARE FOR INTERNAL USE ONLY. THEY'RE ALMOST 4 CERTAIN TO BE SUBJECT TO INCOMPATIBLE CHANGES OR DISAPPEAR COMPLETELY IN 5 FUTURE GNU MP RELEASES. 6 7Copyright 2001-2003 Free Software Foundation, Inc. 8 9This file is part of the GNU MP Library. 10 11The GNU MP Library is free software; you can redistribute it and/or modify 12it under the terms of either: 13 14 * the GNU Lesser General Public License as published by the Free 15 Software Foundation; either version 3 of the License, or (at your 16 option) any later version. 17 18or 19 20 * the GNU General Public License as published by the Free Software 21 Foundation; either version 2 of the License, or (at your option) any 22 later version. 23 24or both in parallel, as here. 25 26The GNU MP Library is distributed in the hope that it will be useful, but 27WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 28or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 29for more details. 30 31You should have received copies of the GNU General Public License and the 32GNU Lesser General Public License along with the GNU MP Library. If not, 33see https://www.gnu.org/licenses/. */ 34 35#define _GNU_SOURCE /* for DECIMAL_POINT in glibc langinfo.h */ 36 37#include "config.h" /* needed for the HAVE_, could also move gmp incls */ 38 39#include <stdarg.h> 40#include <ctype.h> /* for isdigit */ 41#include <stddef.h> /* for ptrdiff_t */ 42#include <string.h> 43#include <stdio.h> /* for NULL */ 44#include <stdlib.h> 45 46#if HAVE_INTTYPES_H 47# include <inttypes.h> /* for intmax_t */ 48#else 49# if HAVE_STDINT_H 50# include <stdint.h> 51# endif 52#endif 53 54#if HAVE_LANGINFO_H 55#include <langinfo.h> /* for nl_langinfo */ 56#endif 57 58#if HAVE_LOCALE_H 59#include <locale.h> /* for localeconv */ 60#endif 61 62#if HAVE_SYS_TYPES_H 63#include <sys/types.h> /* for quad_t */ 64#endif 65 66#include "gmp-impl.h" 67 68 69/* change this to "#define TRACE(x) x" for diagnostics */ 70#define TRACE(x) 71 72 73/* Should be portable, but in any case this is only used under some ASSERTs. */ 74#define va_equal(x, y) \ 75 (memcmp (&(x), &(y), sizeof(va_list)) == 0) 76 77 78/* printf is convenient because it allows various types to be printed in one 79 fairly compact call, so having gmp_printf support the standard types as 80 well as the gmp ones is important. This ends up meaning all the standard 81 parsing must be duplicated, to get a new routine recognising the gmp 82 extras. 83 84 With the currently favoured handling of mpz etc as Z, Q and F type 85 markers, it's not possible to use glibc register_printf_function since 86 that only accepts new conversion characters, not new types. If Z was a 87 conversion there'd be no way to specify hex, decimal or octal, or 88 similarly with F no way to specify fixed point or scientific format. 89 90 It seems wisest to pass conversions %f, %e and %g of float, double and 91 long double over to the standard printf. It'd be hard to be sure of 92 getting the right handling for NaNs, rounding, etc. Integer conversions 93 %d etc and string conversions %s on the other hand could be easily enough 94 handled within gmp_doprnt, but if floats are going to libc then it's just 95 as easy to send all non-gmp types there. 96 97 "Z" was a type marker for size_t in old glibc, but there seems no need to 98 provide access to that now "z" is standard. 99 100 In GMP 4.1.1 we documented "ll" and "L" as being equivalent, but in C99 101 in fact "ll" is just for long long and "L" just for long double. 102 Apparently GLIBC allows "L" for long long though. This doesn't affect 103 us as such, since both are passed through to the C library. To be 104 consistent with what we said before, the two are treated equivalently 105 here, and it's left to the C library to do what it thinks with them. 106 107 Possibilities: 108 109 "b" might be nice for binary output, and could even be supported for the 110 standard C types too if desired. 111 112 POSIX style "%n$" parameter numbering would be possible, but would need 113 to be handled completely within gmp_doprnt, since the numbering will be 114 all different once the format string it cut into pieces. 115 116 Some options for mpq formatting would be good. Perhaps a non-zero 117 precision field could give a width for the denominator and mean always 118 put a "/". A form "n+p/q" might interesting too, though perhaps that's 119 better left to applications. 120 121 Right now there's no way for an application to know whether types like 122 intmax_t are supported here. If configure is doing its job and the same 123 compiler is used for gmp as for the application then there shouldn't be 124 any problem, but perhaps gmp.h should have some preprocessor symbols to 125 say what libgmp can do. */ 126 127 128 129/* If a gmp format is the very first thing or there are two gmp formats with 130 nothing in between then we'll reach here with this_fmt == last_fmt and we 131 can do nothing in that case. 132 133 last_ap is always replaced after a FLUSH, so it doesn't matter if va_list 134 is a call-by-reference and the funs->format routine modifies it. */ 135 136#define FLUSH() \ 137 do { \ 138 if (this_fmt == last_fmt) \ 139 { \ 140 TRACE (printf ("nothing to flush\n")); \ 141 ASSERT (va_equal (this_ap, last_ap)); \ 142 } \ 143 else \ 144 { \ 145 ASSERT (*this_fmt == '%'); \ 146 *this_fmt = '\0'; \ 147 TRACE (printf ("flush \"%s\"\n", last_fmt)); \ 148 DOPRNT_FORMAT (last_fmt, last_ap); \ 149 } \ 150 } while (0) 151 152 153/* Parse up the given format string and do the appropriate output using the 154 given "funs" routines. The data parameter is passed through to those 155 routines. */ 156 157int 158__gmp_doprnt (const struct doprnt_funs_t *funs, void *data, 159 const char *orig_fmt, va_list orig_ap) 160{ 161 va_list ap, this_ap, last_ap; 162 size_t alloc_fmt_size, orig_fmt_size; 163 char *fmt, *alloc_fmt, *last_fmt, *this_fmt, *gmp_str; 164 int retval = 0; 165 int type, fchar, *value, seen_precision; 166 struct doprnt_params_t param; 167 168 TRACE (printf ("gmp_doprnt \"%s\"\n", orig_fmt)); 169 170 /* Don't modify orig_ap, if va_list is actually an array and hence call by 171 reference. It could be argued that it'd be more efficient to leave the 172 caller to make a copy if it cared, but doing so here is going to be a 173 very small part of the total work, and we may as well keep applications 174 out of trouble. */ 175 va_copy (ap, orig_ap); 176 177 /* The format string is chopped up into pieces to be passed to 178 funs->format. Unfortunately that means it has to be copied so each 179 piece can be null-terminated. We're not going to be very fast here, so 180 use __gmp_allocate_func rather than TMP_ALLOC, to avoid overflowing the 181 stack if a long output string is given. */ 182 alloc_fmt_size = orig_fmt_size = strlen (orig_fmt) + 1; 183#if _LONG_LONG_LIMB 184 /* for a long long limb we change %Mx to %llx, so could need an extra 1 185 char for every 3 existing */ 186 alloc_fmt_size += alloc_fmt_size / 3; 187#endif 188 alloc_fmt = __GMP_ALLOCATE_FUNC_TYPE (alloc_fmt_size, char); 189 fmt = alloc_fmt; 190 memcpy (fmt, orig_fmt, orig_fmt_size); 191 192 /* last_fmt and last_ap are just after the last output, and hence where 193 the next output will begin, when that's done */ 194 last_fmt = fmt; 195 va_copy (last_ap, ap); 196 197 for (;;) 198 { 199 TRACE (printf ("next: \"%s\"\n", fmt)); 200 201 fmt = strchr (fmt, '%'); 202 if (fmt == NULL) 203 break; 204 205 /* this_fmt and this_ap are the current '%' sequence being considered */ 206 this_fmt = fmt; 207 va_copy (this_ap, ap); 208 fmt++; /* skip the '%' */ 209 210 TRACE (printf ("considering\n"); 211 printf (" last: \"%s\"\n", last_fmt); 212 printf (" this: \"%s\"\n", this_fmt)); 213 214 type = '\0'; 215 value = ¶m.width; 216 217 param.base = 10; 218 param.conv = 0; 219 param.expfmt = "e%c%02ld"; 220 param.exptimes4 = 0; 221 param.fill = ' '; 222 param.justify = DOPRNT_JUSTIFY_RIGHT; 223 param.prec = 6; 224 param.showbase = DOPRNT_SHOWBASE_NO; 225 param.showpoint = 0; 226 param.showtrailing = 1; 227 param.sign = '\0'; 228 param.width = 0; 229 seen_precision = 0; 230 231 /* This loop parses a single % sequence. "break" from the switch 232 means continue with this %, "goto next" means the conversion 233 character has been seen and a new % should be sought. */ 234 for (;;) 235 { 236 fchar = *fmt++; 237 if (fchar == '\0') 238 break; 239 240 switch (fchar) { 241 242 case 'a': 243 /* %a behaves like %e, but defaults to all significant digits, 244 and there's no leading zeros on the exponent (which is in 245 fact bit-based) */ 246 param.base = 16; 247 param.expfmt = "p%c%ld"; 248 goto conv_a; 249 case 'A': 250 param.base = -16; 251 param.expfmt = "P%c%ld"; 252 conv_a: 253 param.conv = DOPRNT_CONV_SCIENTIFIC; 254 param.exptimes4 = 1; 255 if (! seen_precision) 256 param.prec = -1; /* default to all digits */ 257 param.showbase = DOPRNT_SHOWBASE_YES; 258 param.showtrailing = 1; 259 goto floating_a; 260 261 case 'c': 262 /* Let's assume wchar_t will be promoted to "int" in the call, 263 the same as char will be. */ 264 (void) va_arg (ap, int); 265 goto next; 266 267 case 'd': 268 case 'i': 269 case 'u': 270 integer: 271 TRACE (printf ("integer, base=%d\n", param.base)); 272 if (! seen_precision) 273 param.prec = -1; 274 switch (type) { 275 case 'j': 276 /* Let's assume uintmax_t is the same size as intmax_t. */ 277#if HAVE_INTMAX_T 278 (void) va_arg (ap, intmax_t); 279#else 280 ASSERT_FAIL (intmax_t not available); 281#endif 282 break; 283 case 'l': 284 (void) va_arg (ap, long); 285 break; 286 case 'L': 287#if HAVE_LONG_LONG 288 (void) va_arg (ap, long long); 289#else 290 ASSERT_FAIL (long long not available); 291#endif 292 break; 293 case 'N': 294 { 295 mp_ptr xp; 296 mp_size_t xsize, abs_xsize; 297 mpz_t z; 298 FLUSH (); 299 xp = va_arg (ap, mp_ptr); 300 PTR(z) = xp; 301 xsize = (int) va_arg (ap, mp_size_t); 302 abs_xsize = ABS (xsize); 303 MPN_NORMALIZE (xp, abs_xsize); 304 SIZ(z) = (xsize >= 0 ? abs_xsize : -abs_xsize); 305 ASSERT_CODE (ALLOC(z) = abs_xsize); 306 gmp_str = mpz_get_str (NULL, param.base, z); 307 goto gmp_integer; 308 } 309 /* break; */ 310 case 'q': 311 /* quad_t is probably the same as long long, but let's treat 312 it separately just to be sure. Also let's assume u_quad_t 313 will be the same size as quad_t. */ 314#if HAVE_QUAD_T 315 (void) va_arg (ap, quad_t); 316#else 317 ASSERT_FAIL (quad_t not available); 318#endif 319 break; 320 case 'Q': 321 FLUSH (); 322 gmp_str = mpq_get_str (NULL, param.base, va_arg(ap, mpq_srcptr)); 323 goto gmp_integer; 324 case 't': 325#if HAVE_PTRDIFF_T 326 (void) va_arg (ap, ptrdiff_t); 327#else 328 ASSERT_FAIL (ptrdiff_t not available); 329#endif 330 break; 331 case 'z': 332 (void) va_arg (ap, size_t); 333 break; 334 case 'Z': 335 { 336 int ret; 337 FLUSH (); 338 gmp_str = mpz_get_str (NULL, param.base, 339 va_arg (ap, mpz_srcptr)); 340 gmp_integer: 341 ret = __gmp_doprnt_integer (funs, data, ¶m, gmp_str); 342 __GMP_FREE_FUNC_TYPE (gmp_str, strlen(gmp_str)+1, char); 343 DOPRNT_ACCUMULATE (ret); 344 va_copy (last_ap, ap); 345 last_fmt = fmt; 346 } 347 break; 348 default: 349 /* default is an "int", and this includes h=short and hh=char 350 since they're promoted to int in a function call */ 351 (void) va_arg (ap, int); 352 break; 353 } 354 goto next; 355 356 case 'E': 357 param.base = -10; 358 param.expfmt = "E%c%02ld"; 359 /*FALLTHRU*/ 360 case 'e': 361 param.conv = DOPRNT_CONV_SCIENTIFIC; 362 floating: 363 if (param.showbase == DOPRNT_SHOWBASE_NONZERO) 364 { 365 /* # in %e, %f and %g */ 366 param.showpoint = 1; 367 param.showtrailing = 1; 368 } 369 floating_a: 370 switch (type) { 371 case 'F': 372 FLUSH (); 373 DOPRNT_ACCUMULATE (__gmp_doprnt_mpf (funs, data, ¶m, 374 GMP_DECIMAL_POINT, 375 va_arg (ap, mpf_srcptr))); 376 va_copy (last_ap, ap); 377 last_fmt = fmt; 378 break; 379 case 'L': 380#if HAVE_LONG_DOUBLE 381 (void) va_arg (ap, long double); 382#else 383 ASSERT_FAIL (long double not available); 384#endif 385 break; 386 default: 387 (void) va_arg (ap, double); 388 break; 389 } 390 goto next; 391 392 case 'f': 393 param.conv = DOPRNT_CONV_FIXED; 394 goto floating; 395 396 case 'F': /* mpf_t */ 397 case 'j': /* intmax_t */ 398 case 'L': /* long long */ 399 case 'N': /* mpn */ 400 case 'q': /* quad_t */ 401 case 'Q': /* mpq_t */ 402 case 't': /* ptrdiff_t */ 403 case 'z': /* size_t */ 404 case 'Z': /* mpz_t */ 405 set_type: 406 type = fchar; 407 break; 408 409 case 'G': 410 param.base = -10; 411 param.expfmt = "E%c%02ld"; 412 /*FALLTHRU*/ 413 case 'g': 414 param.conv = DOPRNT_CONV_GENERAL; 415 param.showtrailing = 0; 416 goto floating; 417 418 case 'h': 419 if (type != 'h') 420 goto set_type; 421 type = 'H'; /* internal code for "hh" */ 422 break; 423 424 case 'l': 425 if (type != 'l') 426 goto set_type; 427 type = 'L'; /* "ll" means "L" */ 428 break; 429 430 case 'm': 431 /* glibc strerror(errno), no argument */ 432 goto next; 433 434 case 'M': /* mp_limb_t */ 435 /* mung format string to l or ll and let plain printf handle it */ 436#if _LONG_LONG_LIMB 437 memmove (fmt+1, fmt, strlen (fmt)+1); 438 fmt[-1] = 'l'; 439 fmt[0] = 'l'; 440 fmt++; 441 type = 'L'; 442#else 443 fmt[-1] = 'l'; 444 type = 'l'; 445#endif 446 break; 447 448 case 'n': 449 { 450 void *p; 451 FLUSH (); 452 p = va_arg (ap, void *); 453 switch (type) { 454 case '\0': * (int *) p = retval; break; 455 case 'F': mpf_set_si ((mpf_ptr) p, (long) retval); break; 456 case 'H': * (char *) p = retval; break; 457 case 'h': * (short *) p = retval; break; 458#if HAVE_INTMAX_T 459 case 'j': * (intmax_t *) p = retval; break; 460#else 461 case 'j': ASSERT_FAIL (intmax_t not available); break; 462#endif 463 case 'l': * (long *) p = retval; break; 464#if HAVE_QUAD_T && HAVE_LONG_LONG 465 case 'q': 466 ASSERT_ALWAYS (sizeof (quad_t) == sizeof (long long)); 467 /*FALLTHRU*/ 468#else 469 case 'q': ASSERT_FAIL (quad_t not available); break; 470#endif 471#if HAVE_LONG_LONG 472 case 'L': * (long long *) p = retval; break; 473#else 474 case 'L': ASSERT_FAIL (long long not available); break; 475#endif 476 case 'N': 477 { 478 mp_size_t n; 479 n = va_arg (ap, mp_size_t); 480 n = ABS (n); 481 if (n != 0) 482 { 483 * (mp_ptr) p = retval; 484 MPN_ZERO ((mp_ptr) p + 1, n - 1); 485 } 486 } 487 break; 488 case 'Q': mpq_set_si ((mpq_ptr) p, (long) retval, 1L); break; 489#if HAVE_PTRDIFF_T 490 case 't': * (ptrdiff_t *) p = retval; break; 491#else 492 case 't': ASSERT_FAIL (ptrdiff_t not available); break; 493#endif 494 case 'z': * (size_t *) p = retval; break; 495 case 'Z': mpz_set_si ((mpz_ptr) p, (long) retval); break; 496 } 497 } 498 va_copy (last_ap, ap); 499 last_fmt = fmt; 500 goto next; 501 502 case 'o': 503 param.base = 8; 504 goto integer; 505 506 case 'p': 507 case 's': 508 /* "void *" will be good enough for "char *" or "wchar_t *", no 509 need for separate code. */ 510 (void) va_arg (ap, const void *); 511 goto next; 512 513 case 'x': 514 param.base = 16; 515 goto integer; 516 case 'X': 517 param.base = -16; 518 goto integer; 519 520 case '%': 521 goto next; 522 523 case '#': 524 param.showbase = DOPRNT_SHOWBASE_NONZERO; 525 break; 526 527 case '\'': 528 /* glibc digit grouping, just pass it through, no support for it 529 on gmp types */ 530 break; 531 532 case '+': 533 case ' ': 534 param.sign = fchar; 535 break; 536 537 case '-': 538 param.justify = DOPRNT_JUSTIFY_LEFT; 539 break; 540 case '.': 541 seen_precision = 1; 542 param.prec = -1; /* "." alone means all necessary digits */ 543 value = ¶m.prec; 544 break; 545 546 case '*': 547 { 548 int n = va_arg (ap, int); 549 550 if (value == ¶m.width) 551 { 552 /* negative width means left justify */ 553 if (n < 0) 554 { 555 param.justify = DOPRNT_JUSTIFY_LEFT; 556 n = -n; 557 } 558 param.width = n; 559 } 560 else 561 { 562 /* don't allow negative precision */ 563 param.prec = MAX (0, n); 564 } 565 } 566 break; 567 568 case '0': 569 if (value == ¶m.width) 570 { 571 /* in width field, set fill */ 572 param.fill = '0'; 573 574 /* for right justify, put the fill after any minus sign */ 575 if (param.justify == DOPRNT_JUSTIFY_RIGHT) 576 param.justify = DOPRNT_JUSTIFY_INTERNAL; 577 } 578 else 579 { 580 /* in precision field, set value */ 581 *value = 0; 582 } 583 break; 584 585 case '1': case '2': case '3': case '4': case '5': 586 case '6': case '7': case '8': case '9': 587 /* process all digits to form a value */ 588 { 589 int n = 0; 590 do { 591 n = n * 10 + (fchar-'0'); 592 fchar = *fmt++; 593 } while (isascii (fchar) && isdigit (fchar)); 594 fmt--; /* unget the non-digit */ 595 *value = n; 596 } 597 break; 598 599 default: 600 /* something invalid */ 601 ASSERT (0); 602 goto next; 603 } 604 } 605 606 next: 607 /* Stop parsing the current "%" format, look for a new one. */ 608 ; 609 } 610 611 TRACE (printf ("remainder: \"%s\"\n", last_fmt)); 612 if (*last_fmt != '\0') 613 DOPRNT_FORMAT (last_fmt, last_ap); 614 615 if (funs->final != NULL) 616 if ((*funs->final) (data) == -1) 617 goto error; 618 619 done: 620 __GMP_FREE_FUNC_TYPE (alloc_fmt, alloc_fmt_size, char); 621 return retval; 622 623 error: 624 retval = -1; 625 goto done; 626} 627