github.com/aergoio/aergo@v1.3.1/libtool/src/gmp-6.1.2/printf/doprnt.c (about)

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