Home | History | Annotate | Line # | Download | only in printf
      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 
      7 Copyright 2001-2003 Free Software Foundation, Inc.
      8 
      9 This file is part of the GNU MP Library.
     10 
     11 The GNU MP Library is free software; you can redistribute it and/or modify
     12 it 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 
     18 or
     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 
     24 or both in parallel, as here.
     25 
     26 The GNU MP Library is distributed in the hope that it will be useful, but
     27 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
     28 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     29 for more details.
     30 
     31 You should have received copies of the GNU General Public License and the
     32 GNU Lesser General Public License along with the GNU MP Library.  If not,
     33 see 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 
    157 int
    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 = &param.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, &param, 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, &param,
    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 = &param.prec;
    544 	    break;
    545 
    546 	  case '*':
    547 	    {
    548 	      int n = va_arg (ap, int);
    549 
    550 	      if (value == &param.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 == &param.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